dlfcn removed and minor code cleanups

This commit is contained in:
Thakee Nathees 2022-04-20 09:40:54 +05:30
parent 2262089948
commit 1f8a71fe0a
14 changed files with 142 additions and 999 deletions

View File

@ -35,15 +35,3 @@
// Source : https://github.com/cofyc/argparse/
// About : Command-line arguments parsing library.
#include "thirdparty/argparse/argparse.c"
// Library : dlfcn-win32
// License : MIT
// Source : https://github.com/dlfcn-win32/dlfcn-win32/
// About : An implementation of dlfcn for Windows.
#ifdef _WIN32
// FIXME:
// This library redefine the malloc family macro, which cause a compile
// time warning.
//
// #include "modules/thirdparty/dlfcn-win32/dlfcn.c"
#endif

View File

@ -1,17 +0,0 @@
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.

View File

@ -1,785 +0,0 @@
/*
* 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 HMODULE MyGetModuleHandleFromAddress( 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 *)) 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, (LPCSTR) 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)) 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 = SetErrorMode( SEM_FAILCRITICALERRORS );
psapi = LoadLibraryA( "Psapi.dll" );
if( psapi != NULL )
{
EnumProcessModulesPtr = (BOOL (WINAPI *)(HANDLE, HMODULE *, DWORD, LPDWORD)) GetProcAddress( psapi, "EnumProcessModules" );
if( EnumProcessModulesPtr == NULL )
FreeLibrary( psapi );
}
SetErrorMode( 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 = SetErrorMode( 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. */
SetErrorMode( 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 = 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_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 from export table */
static const char *get_export_symbol_name( HMODULE module, IMAGE_EXPORT_DIRECTORY *ied, 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( 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_info( 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 : addr;
return TRUE;
}
DLFCN_EXPORT
int dladdr( 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

View File

@ -1,94 +0,0 @@
/*
* 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(void *addr, Dl_info *info);
#ifdef __cplusplus
}
#endif
#endif /* DLFCN_H */

View File

@ -12,7 +12,8 @@ from os.path import (join, exists, abspath,
THIS_PATH = abspath(dirname(__file__))
POCKET_HEADER = join(THIS_PATH, "../src/include/pocketlang.h")
TARGET = join(THIS_PATH, "./modules/pknative.gen.c")
TARGET_NATIVE = join(THIS_PATH, "./modules/pknative.gen.c")
TARGET_DL = join(THIS_PATH, "./modules/std_dl_api.gen.h")
PK_API = "pk_api"
PK_API_TYPE = "PkNativeApi"
@ -109,15 +110,37 @@ def init_api(api_functions):
assign += f"\n {PK_API}.{fn}_ptr = api->{fn}_ptr;"
return API_DEF % assign + '\n'
def make_api(api_functions):
source = "#if defined(NATIVE_API_IMPLEMENT)\n\n"
source += f"{PK_API_TYPE} dlMakeApi() {{\n\n"
source += f" {PK_API_TYPE} api;\n\n"
for fn, params, ret in api_functions:
source += f" api.{fn}_ptr = {fn};\n"
source += "\n"
source += " return api;\n"
source += "}\n\n"
source += "#endif // NATIVE_API_IMPLEMENT\n\n"
return source
def generate():
api_functions = get_api_functions()
with open(TARGET, 'w') as fp:
## Generate pocket native api.
with open(TARGET_NATIVE, 'w') as fp:
fp.write(SOURCE_GEN)
fp.write(fn_typedefs(api_functions))
fp.write(api_typedef(api_functions))
fp.write(init_api(api_functions))
fp.write(define_functions(api_functions))
## Generate dl module api definition.
with open(TARGET_DL, 'w') as fp:
fp.write(SOURCE_GEN)
fp.write(fn_typedefs(api_functions))
fp.write(api_typedef(api_functions))
fp.write(make_api(api_functions))
if __name__ == "__main__":
generate()
print("Generated:", relpath(TARGET, os.getcwd()))
print("Generated:", relpath(TARGET_NATIVE, os.getcwd()))
print("Generated:", relpath(TARGET_DL, os.getcwd()))

View File

@ -133,7 +133,7 @@ int repl(PKVM* vm, const PkCompileOptions* options) {
if (result != PK_RESULT_SUCCESS) continue;
// Compiled source would be the "main" function of the module. Run it.
PkHandle* _main = pkGetMainFunction(vm, module);
PkHandle* _main = pkModuleGetMainFunction(vm, module);
PkHandle* fiber = pkNewFiber(vm, _main);
ASSERT((_main != NULL) && (fiber != NULL), OOPS);

View File

@ -74,9 +74,11 @@ typedef struct PkHandle PkHandle;
// alive use `pkNewHandle()`.
typedef void* PkVar;
// Type enum of the pocketlang variables, this can be used to get the type
// from a PkVar in the method pkGetVarType().
// Type enum of the pocketlang's first class types. Note that Object isn't
// instanciable (as of now) but they're considered first calss.
typedef enum {
PK_OBJECT = 0,
PK_NULL,
PK_BOOL,
PK_NUMBER,
@ -85,10 +87,10 @@ typedef enum {
PK_MAP,
PK_RANGE,
PK_MODULE,
PK_FUNCTION,
PK_CLOSURE,
PK_FIBER,
PK_CLASS,
PK_INST,
PK_INSTANCE,
} PkVarType;
typedef struct PkStringPtr PkStringPtr;
@ -257,7 +259,7 @@ PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
// Returns the main function of the [module]. When a module is compiled all of
// it's statements are wrapped around an implicit main function.
PK_PUBLIC PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module);
PK_PUBLIC PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module);
// Compile the [module] with the provided [source]. Set the compiler options
// with the the [options] argument or set to NULL for default options.

View File

@ -237,7 +237,6 @@ typedef struct {
} GrammarRule;
typedef enum {
DEPTH_MODULE = -2, //< Only used for module body function's depth.
DEPTH_GLOBAL = -1, //< Global variables.
DEPTH_LOCAL, //< Local scope. Increase with inner scope.
} Depth;
@ -1249,9 +1248,9 @@ static bool matchAssignment(Compiler* compiler) {
static int findBuiltinFunction(const PKVM* vm,
const char* name, uint32_t length) {
for (int i = 0; i < vm->builtins_count; i++) {
uint32_t bfn_length = (uint32_t)strlen(vm->builtins[i]->fn->name);
uint32_t bfn_length = (uint32_t)strlen(vm->builtins_funcs[i]->fn->name);
if (bfn_length != length) continue;
if (strncmp(name, vm->builtins[i]->fn->name, length) == 0) {
if (strncmp(name, vm->builtins_funcs[i]->fn->name, length) == 0) {
return i;
}
}
@ -3142,10 +3141,6 @@ PkResult compile(PKVM* vm, Module* module, const char* source,
Func curr_fn;
compilerPushFunc(compiler, &curr_fn, module->body->fn);
// At the begining the compiler's scope will be DEPTH_GLOBAL and that'll be
// set to to the current functions depth. Override for the body function.
curr_fn.depth = DEPTH_MODULE;
// Lex initial tokens. current <-- next.
lexToken(&(compiler->parser));
lexToken(&(compiler->parser));

View File

@ -46,35 +46,41 @@ static void moduleAddFunctionInternal(PKVM* vm, Module* module,
/* CORE PUBLIC API */
/*****************************************************************************/
#define CHECK_NULL(name) \
ASSERT(name != NULL, "Argument " #name " was NULL.");
#define CHECK_TYPE(handle, type) \
do { \
CHECK_NULL(handle); \
ASSERT(IS_OBJ_TYPE(handle->value, type), \
"Given handle is not of type " #type "."); \
} while (false)
PkHandle* pkNewModule(PKVM* vm, const char* name) {
CHECK_NULL(name);
Module* module = newModuleInternal(vm, name);
return vmNewHandle(vm, VAR_OBJ(module));
}
void pkRegisterModule(PKVM* vm, PkHandle* module) {
ASSERT(module != NULL, "Argument module was NULL.");
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
CHECK_TYPE(module, OBJ_MODULE);
Module* module_ = (Module*)AS_OBJ(module->value);
vmRegisterModule(vm, module_, module_->name);
}
void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
const char* name, PkHandle* value) {
ASSERT(module != NULL, "Argument module was NULL.");
ASSERT(value != NULL, "Argument value was NULL.");
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
CHECK_TYPE(module, OBJ_MODULE);
CHECK_NULL(value);
moduleAddGlobal(vm, (Module*)AS_OBJ(module->value),
name, (uint32_t)strlen(name), value->value);
}
PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, const char* name) {
ASSERT(module != NULL, "Argument module was NULL.");
ASSERT(name != NULL, "Argument name was NULL.");
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
CHECK_TYPE(module, OBJ_MODULE);
CHECK_NULL(name);
Module* module_ = (Module*)AS_OBJ(module->value);
int index = moduleGetGlobalIndex(module_, name, (uint32_t)strlen(name));
@ -84,18 +90,17 @@ PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, const char* name) {
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
pkNativeFn fptr, int arity) {
ASSERT(module != NULL, "Argument module was NULL.");
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
CHECK_TYPE(module, OBJ_MODULE);
CHECK_NULL(fptr);
moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value),
name, fptr, arity,
NULL /*TODO: Public API for function docstring.*/);
}
PkHandle* pkGetMainFunction(PKVM* vm, PkHandle* module) {
ASSERT(module != NULL, "Argument module was NULL.");
ASSERT(IS_OBJ_TYPE(module->value, OBJ_MODULE),
"Given handle is not a module.");
PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module) {
CHECK_TYPE(module, OBJ_MODULE);
Module* _module = (Module*)AS_OBJ(module->value);
int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME,
@ -321,6 +326,9 @@ bool pkFiberIsDone(const PkHandle* fiber) {
return _fiber->state == FIBER_DONE;
}
#undef CHECK_NULL
#undef CHECK_TYPE
/*****************************************************************************/
/* VALIDATORS */
/*****************************************************************************/
@ -766,8 +774,8 @@ static void initializeBuiltinFN(PKVM* vm, Closure** bfn, const char* name,
}
static void initializeBuiltinFunctions(PKVM* vm) {
#define INITIALIZE_BUILTIN_FN(name, fn, argc) \
initializeBuiltinFN(vm, &vm->builtins[vm->builtins_count++], name, \
#define INITIALIZE_BUILTIN_FN(name, fn, argc) \
initializeBuiltinFN(vm, &vm->builtins_funcs[vm->builtins_count++], name, \
(int)strlen(name), argc, fn, DOCSTRING(fn));
// General functions.
INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1);
@ -1467,8 +1475,8 @@ bool varContains(PKVM* vm, Var elem, Var container) {
Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
#define ERR_NO_ATTRIB(vm, on, attrib) \
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'", \
#define ERR_NO_ATTRIB(vm, on, attrib) \
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'.", \
varTypeName(on), attrib->data))
if (!IS_OBJ(on)) {

View File

@ -227,7 +227,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
{
int index = READ_BYTE();
ASSERT_INDEX(index, vm->builtins_count);
const char* name = vm->builtins[index]->fn->name;
const char* name = vm->builtins_funcs[index]->fn->name;
// Prints: %5d [Fn:%s]\n
PRINT_INT(index);
PRINT(" [Fn:");

View File

@ -18,29 +18,18 @@
PkVarType pkGetValueType(const PkVar value) {
__ASSERT(value != NULL, "Given value was NULL.");
const Var value_ = *(const Var*)(value);
if (IS_NULL(*(const Var*)(value))) return PK_NULL;
if (IS_BOOL(*(const Var*)(value))) return PK_BOOL;
if (IS_NUM(*(const Var*)(value))) return PK_NUMBER;
if (IS_NULL(value_)) return PK_NULL;
if (IS_BOOL(value_)) return PK_BOOL;
if (IS_NUM(value_)) return PK_NUMBER;
__ASSERT(IS_OBJ(*(const Var*)(value)),
"Invalid var pointer. Might be a dangling pointer");
ASSERT(IS_OBJ(*(const Var*)(value)),
"Invalid var pointer (Might be a dangling pointer).");
const Object* obj = AS_OBJ(*(const Var*)(value));
switch (obj->type) {
case OBJ_STRING: return PK_STRING;
case OBJ_LIST: return PK_LIST;
case OBJ_MAP: return PK_MAP;
case OBJ_RANGE: return PK_RANGE;
case OBJ_MODULE: return PK_MODULE;
case OBJ_FUNC: return PK_FUNCTION;
case OBJ_FIBER: return PK_FIBER;
case OBJ_CLASS: return PK_CLASS;
case OBJ_INST: return PK_INST;
}
const Object* obj = AS_OBJ(value_);
return getObjPkVarType(obj->type);
UNREACHABLE();
return PK_NULL;
}
PkHandle* pkNewString(PKVM* vm, const char* value) {
@ -525,6 +514,7 @@ Class* newClass(PKVM* vm, Module* module, const char* name, uint32_t length,
pkUintBufferInit(&cls->field_names);
cls->owner = module;
cls->docstring = NULL;
cls->name = moduleAddString(module, vm, name, length, NULL);
// Since characters '@' and '$' are special in stringFormat, and they
@ -1333,24 +1323,56 @@ bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
/* UTILITY FUNCTIONS */
/*****************************************************************************/
PkVarType getObjPkVarType(ObjectType type) {
switch (type) {
case OBJ_STRING: return PK_STRING;
case OBJ_LIST: return PK_LIST;
case OBJ_MAP: return PK_MAP;
case OBJ_RANGE: return PK_RANGE;
case OBJ_MODULE: return PK_MODULE;
case OBJ_FUNC: UNREACHABLE();
case OBJ_CLOSURE: return PK_CLOSURE;
case OBJ_UPVALUE: UNREACHABLE();
case OBJ_FIBER: return PK_FIBER;
case OBJ_CLASS: return PK_CLASS;
case OBJ_INST: return PK_INSTANCE;
}
UNREACHABLE();
return (PkVarType) -1;
}
ObjectType getPkVarObjType(PkVarType type) {
switch (type) {
case PK_OBJECT:
case PK_NULL:
case PK_BOOL:
case PK_NUMBER:
UNREACHABLE();
case PK_STRING: return OBJ_STRING;
case PK_LIST: return OBJ_LIST;
case PK_MAP: return OBJ_MAP;
case PK_RANGE: return OBJ_RANGE;
case PK_MODULE: return OBJ_MODULE;
case PK_CLOSURE: return OBJ_CLOSURE;
case PK_FIBER: return OBJ_FIBER;
case PK_CLASS: return OBJ_CLASS;
case PK_INSTANCE: return OBJ_INST;
}
UNREACHABLE();
return (ObjectType) -1;
}
const char* getPkVarTypeName(PkVarType type) {
switch (type) {
case PK_OBJECT: return "Object";
case PK_NULL: return "Null";
case PK_BOOL: return "Bool";
case PK_NUMBER: return "Number";
case PK_STRING: return "String";
case PK_LIST: return "List";
case PK_MAP: return "Map";
case PK_RANGE: return "Range";
case PK_MODULE: return "Module";
// TODO: since functions are not first class citizens anymore, remove it
// and add closure (maybe with the same name PK_FUNCTION).
case PK_FUNCTION: return "Function";
case PK_FIBER: return "Fiber";
case PK_CLASS: return "Class";
case PK_INST: return "Inst";
default:
return getObjectTypeName(getPkVarObjType(type));
}
UNREACHABLE();

View File

@ -344,9 +344,8 @@ struct Function {
// is prevent checking is_native everytime (which might be a bit faster).
int upvalue_count;
// Docstring of the function, currently it's just the C string literal
// pointer, refactor this into String* so that we can support public
// native functions to provide a docstring.
// Docstring of the function. Could be either a C string literal or a string
// entry in it's owner module's constant pool.
const char* docstring;
// Function can be either native C function pointers or compiled pocket
@ -486,6 +485,10 @@ struct Class {
// Name of the class.
String* name;
// Docstring of the class. Could be either a C string literal or a string
// entry in it's owner module's constant pool.
const char* docstring;
Closure* ctor; //< The constructor function.
pkUintBuffer field_names; //< Buffer of field names.
// TODO: ordered names buffer for binary search.
@ -592,10 +595,6 @@ void markValue(PKVM* vm, Var self);
// the garbage collection.
void markVarBuffer(PKVM* vm, pkVarBuffer* self);
// Mark the elements of the buffer as reachable at the mark-and-sweep phase of
// the garbage collection.
void markStringBuffer(PKVM* vm, pkStringBuffer* self);
// Pop the marked objects from the working set of the VM and add it's
// referenced objects to the working set, continue traversing and mark
// all the reachable objects.
@ -724,6 +723,12 @@ Var doubleToVar(double value);
// Internal method behind AS_NUM(value) don't use it directly.
double varToDouble(Var value);
// Returns the PkVarType of the object type.
PkVarType getObjPkVarType(ObjectType type);
// Returns the ObjectType of the PkVar type.
ObjectType getPkVarObjType(PkVarType type);
// Returns the type name of the PkVarType enum value.
const char* getPkVarTypeName(PkVarType type);

View File

@ -73,8 +73,8 @@ PKVM* pkNewVM(PkConfiguration* config) {
// This is necessary to prevent garbage collection skip the entry in this
// array while we're building it.
for (int i = 0; i < OBJ_INST; i++) {
vm->primitives[i] = NULL;
for (int i = 0; i < PK_INSTANCE; i++) {
vm->builtin_classes[i] = NULL;
}
initializeCore(vm);
@ -272,20 +272,16 @@ void vmCollectGarbage(PKVM* vm) {
// Mark builtin functions.
for (int i = 0; i < vm->builtins_count; i++) {
markObject(vm, &vm->builtins[i]->_super);
markObject(vm, &vm->builtins_funcs[i]->_super);
}
// Mark primitive types' classes.
for (int i = 0; i < (int)OBJ_INST; i++) {
// Upvalue and functions aren't first class objects and they doesn't
// require classes.
if (i == OBJ_UPVALUE || i == OBJ_FUNC) continue;
for (int i = 0; i < PK_INSTANCE; i++) {
// It's possible that a garbage collection could be triggered while we're
// building the primitives and the class could be NULL.
if (vm->primitives[i] == NULL) continue;
if (vm->builtin_classes[i] == NULL) continue;
markObject(vm, &vm->primitives[i]->_super);
markObject(vm, &vm->builtin_classes[i]->_super);
}
// Mark the modules.
@ -970,7 +966,7 @@ L_vm_main_loop:
{
uint8_t index = READ_BYTE();
ASSERT_INDEX(index, vm->builtins_count);
Closure* closure = vm->builtins[index];
Closure* closure = vm->builtins_funcs[index];
PUSH(VAR_OBJ(closure));
DISPATCH();
}

View File

@ -114,13 +114,13 @@ struct PKVM {
Map* modules;
// Array of all builtin functions.
Closure* builtins[BUILTIN_FN_CAPACITY];
Closure* builtins_funcs[BUILTIN_FN_CAPACITY];
int builtins_count;
// An array of all the primitive types' class except for OBJ_INST. Since the
// type of the objects are enums starting from 0 we can directly get the
// class by using their enum (ex: primitives[OBJ_LIST]).
Class* primitives[(int)OBJ_INST];
Class* builtin_classes[PK_INSTANCE];
// Current fiber.
Fiber* fiber;