2011-03-08 22:00:00 +01:00
|
|
|
|
//=======================================================================
|
|
|
|
|
// Copyright XashXT Group 2008 <20>
|
|
|
|
|
// library.c - custom dlls loader
|
|
|
|
|
//=======================================================================
|
|
|
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
|
#include "library.h"
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
---------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
Name for function stuff
|
|
|
|
|
|
|
|
|
|
---------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
static void FsGetString( file_t *f, char *str )
|
|
|
|
|
{
|
|
|
|
|
char ch;
|
|
|
|
|
|
|
|
|
|
while(( ch = FS_Getc( f )) != EOF )
|
|
|
|
|
{
|
|
|
|
|
*str++ = ch;
|
|
|
|
|
if( !ch ) break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void FreeNameFuncGlobals( dll_user_t *hInst )
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if( !hInst ) return;
|
|
|
|
|
|
|
|
|
|
if( hInst->ordinals ) Mem_Free( hInst->ordinals );
|
|
|
|
|
if( hInst->funcs ) Mem_Free( hInst->funcs );
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
|
|
|
{
|
|
|
|
|
if( hInst->names[i] )
|
|
|
|
|
Mem_Free( hInst->names[i] );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hInst->num_ordinals = 0;
|
|
|
|
|
hInst->ordinals = NULL;
|
|
|
|
|
hInst->funcs = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char *GetMSVCName( const char *in_name )
|
|
|
|
|
{
|
|
|
|
|
char *pos, *out_name;
|
|
|
|
|
|
|
|
|
|
if( in_name[0] == '?' ) // is this a MSVC C++ mangled name?
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
if(( pos = Q_strstr( in_name, "@@" )) != NULL )
|
2011-03-08 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
int len = pos - in_name;
|
|
|
|
|
|
|
|
|
|
// strip off the leading '?'
|
|
|
|
|
out_name = copystring( in_name + 1 );
|
|
|
|
|
out_name[len-1] = 0; // terminate string at the "@@"
|
|
|
|
|
return out_name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return copystring( in_name );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
qboolean LibraryLoadSymbols( dll_user_t *hInst )
|
|
|
|
|
{
|
|
|
|
|
file_t *f;
|
|
|
|
|
string errorstring;
|
|
|
|
|
DOS_HEADER dos_header;
|
|
|
|
|
LONG nt_signature;
|
|
|
|
|
PE_HEADER pe_header;
|
|
|
|
|
SECTION_HEADER section_header;
|
|
|
|
|
qboolean edata_found;
|
|
|
|
|
OPTIONAL_HEADER optional_header;
|
|
|
|
|
long edata_offset;
|
|
|
|
|
long edata_delta;
|
|
|
|
|
EXPORT_DIRECTORY export_directory;
|
|
|
|
|
long name_offset;
|
|
|
|
|
long ordinal_offset;
|
|
|
|
|
long function_offset;
|
|
|
|
|
string function_name;
|
|
|
|
|
dword *p_Names = NULL;
|
|
|
|
|
int i, index;
|
|
|
|
|
|
|
|
|
|
// can only be done for loaded libraries
|
|
|
|
|
if( !hInst ) return false;
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
|
|
|
hInst->names[i] = NULL;
|
|
|
|
|
|
|
|
|
|
f = FS_Open( hInst->shortPath, "rb", false );
|
|
|
|
|
if( !f )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "couldn't load %s", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, &dos_header, sizeof( dos_header )) != sizeof( dos_header ))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s has corrupted EXE header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( dos_header.e_magic != DOS_SIGNATURE )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid dll signature", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Seek( f, dos_header.e_lfanew, SEEK_SET ) == -1 )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s error seeking for new exe header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, &nt_signature, sizeof( nt_signature )) != sizeof( nt_signature ))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s has corrupted NT header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( nt_signature != NT_SIGNATURE )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid NT signature", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, &pe_header, sizeof( pe_header )) != sizeof( pe_header ))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid PE header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( !pe_header.SizeOfOptionalHeader )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have an optional header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, &optional_header, sizeof( optional_header )) != sizeof( optional_header ))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s optional header probably corrupted", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
edata_found = false;
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < pe_header.NumberOfSections; i++ )
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, §ion_header, sizeof( section_header )) != sizeof( section_header ))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s error during reading section header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-09 22:00:00 +01:00
|
|
|
|
if( !Q_strcmp((char *)section_header.Name, ".edata" ))
|
2011-03-08 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
edata_found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( edata_found )
|
|
|
|
|
{
|
|
|
|
|
edata_offset = section_header.PointerToRawData;
|
|
|
|
|
edata_delta = section_header.VirtualAddress - section_header.PointerToRawData;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
edata_offset = optional_header.DataDirectory[0].VirtualAddress;
|
|
|
|
|
edata_delta = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Seek( f, edata_offset, SEEK_SET ) == -1 )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid exports section", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, &export_directory, sizeof( export_directory )) != sizeof( export_directory ))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid optional header", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hInst->num_ordinals = export_directory.NumberOfNames; // also number of ordinals
|
|
|
|
|
|
|
|
|
|
if( hInst->num_ordinals > MAX_LIBRARY_EXPORTS )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s too many exports", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ordinal_offset = export_directory.AddressOfNameOrdinals - edata_delta;
|
|
|
|
|
|
|
|
|
|
if( FS_Seek( f, ordinal_offset, SEEK_SET ) == -1 )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid ordinals section", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hInst->ordinals = Mem_Alloc( host.mempool, hInst->num_ordinals * sizeof( word ));
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, hInst->ordinals, hInst->num_ordinals * sizeof( word )) != (hInst->num_ordinals * sizeof( word )))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s error during reading ordinals table", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function_offset = export_directory.AddressOfFunctions - edata_delta;
|
|
|
|
|
if( FS_Seek( f, function_offset, SEEK_SET ) == -1 )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s does not have a valid export address section", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hInst->funcs = Mem_Alloc( host.mempool, hInst->num_ordinals * sizeof( dword ));
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, hInst->funcs, hInst->num_ordinals * sizeof( dword )) != (hInst->num_ordinals * sizeof( dword )))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s error during reading export address section", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
name_offset = export_directory.AddressOfNames - edata_delta;
|
|
|
|
|
|
|
|
|
|
if( FS_Seek( f, name_offset, SEEK_SET ) == -1 )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s file does not have a valid names section", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p_Names = Mem_Alloc( host.mempool, hInst->num_ordinals * sizeof( dword ));
|
|
|
|
|
|
|
|
|
|
if( FS_Read( f, p_Names, hInst->num_ordinals * sizeof( dword )) != (hInst->num_ordinals * sizeof( dword )))
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s error during reading names table", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
|
|
|
{
|
|
|
|
|
name_offset = p_Names[i] - edata_delta;
|
|
|
|
|
|
|
|
|
|
if( name_offset != 0 )
|
|
|
|
|
{
|
|
|
|
|
if( FS_Seek( f, name_offset, SEEK_SET ) != -1 )
|
|
|
|
|
{
|
|
|
|
|
FsGetString( f, function_name );
|
|
|
|
|
hInst->names[i] = GetMSVCName( function_name );
|
|
|
|
|
}
|
|
|
|
|
else break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( i != hInst->num_ordinals )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
Q_sprintf( errorstring, "%s error during loading names section", hInst->shortPath );
|
2011-03-08 22:00:00 +01:00
|
|
|
|
goto table_error;
|
|
|
|
|
}
|
|
|
|
|
FS_Close( f );
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
if( !Q_strcmp( "GiveFnptrsToDll", hInst->names[i] )) // main entry point for user dlls
|
2011-03-08 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
void *fn_offset;
|
|
|
|
|
|
|
|
|
|
index = hInst->ordinals[i];
|
|
|
|
|
fn_offset = (void *)Com_GetProcAddress( hInst, "GiveFnptrsToDll" );
|
|
|
|
|
hInst->funcBase = (dword)(fn_offset) - hInst->funcs[index];
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( p_Names ) Mem_Free( p_Names );
|
|
|
|
|
return true;
|
|
|
|
|
table_error:
|
|
|
|
|
// cleanup
|
|
|
|
|
if( f ) FS_Close( f );
|
|
|
|
|
if( p_Names ) Mem_Free( p_Names );
|
|
|
|
|
FreeNameFuncGlobals( hInst );
|
|
|
|
|
MsgDev( D_ERROR, "LoadLibrary: %s\n", errorstring );
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
================
|
|
|
|
|
Com_LoadLibrary
|
|
|
|
|
|
|
|
|
|
smart dll loader - can loading dlls from pack or wad files
|
|
|
|
|
================
|
|
|
|
|
*/
|
|
|
|
|
void *Com_LoadLibraryExt( const char *dllname, int build_ordinals_table, qboolean directpath )
|
|
|
|
|
{
|
|
|
|
|
dll_user_t *hInst;
|
|
|
|
|
|
|
|
|
|
hInst = FS_FindLibrary( dllname, directpath );
|
|
|
|
|
if( !hInst ) return NULL; // nothing to load
|
|
|
|
|
|
|
|
|
|
if( hInst->custom_loader )
|
|
|
|
|
{
|
|
|
|
|
if( hInst->encrypted )
|
|
|
|
|
MsgDev( D_ERROR, "Sys_LoadLibrary: couldn't load encrypted library %s\n", dllname );
|
|
|
|
|
else MsgDev( D_ERROR, "Sys_LoadLibrary: couldn't load library %s from packfile\n", dllname );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
else hInst->hInstance = LoadLibrary( hInst->fullPath );
|
|
|
|
|
|
|
|
|
|
if( !hInst->hInstance )
|
|
|
|
|
{
|
|
|
|
|
MsgDev( D_NOTE, "Sys_LoadLibrary: Loading %s - failed\n", dllname );
|
|
|
|
|
Com_FreeLibrary( hInst );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if not set - FunctionFromName and NameForFunction will not working
|
|
|
|
|
if( build_ordinals_table )
|
|
|
|
|
{
|
|
|
|
|
if( !LibraryLoadSymbols( hInst ))
|
|
|
|
|
{
|
|
|
|
|
MsgDev( D_NOTE, "Sys_LoadLibrary: Loading %s - failed\n", dllname );
|
|
|
|
|
Com_FreeLibrary( hInst );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MsgDev( D_NOTE, "Sys_LoadLibrary: Loading %s - ok\n", dllname );
|
|
|
|
|
|
|
|
|
|
return hInst;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *Com_LoadLibrary( const char *dllname, int build_ordinals_table )
|
|
|
|
|
{
|
|
|
|
|
return Com_LoadLibraryExt( dllname, build_ordinals_table, false );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *Com_GetProcAddress( void *hInstance, const char *name )
|
|
|
|
|
{
|
|
|
|
|
dll_user_t *hInst = (dll_user_t *)hInstance;
|
|
|
|
|
|
|
|
|
|
if( !hInst || !hInst->hInstance )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
if( !hInst->custom_loader )
|
|
|
|
|
return GetProcAddress( hInst->hInstance, name );
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Com_FreeLibrary( void *hInstance )
|
|
|
|
|
{
|
|
|
|
|
dll_user_t *hInst = (dll_user_t *)hInstance;
|
|
|
|
|
|
|
|
|
|
if( !hInst || !hInst->hInstance )
|
|
|
|
|
return; // already freed
|
|
|
|
|
|
|
|
|
|
if( host.state == HOST_CRASHED )
|
|
|
|
|
{
|
|
|
|
|
// we need to hold down all modules, while MSVC can find error
|
|
|
|
|
MsgDev( D_NOTE, "Sys_FreeLibrary: hold %s for debugging\n", hInst->dllName );
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else MsgDev( D_NOTE, "Sys_FreeLibrary: Unloading %s\n", hInst->dllName );
|
|
|
|
|
|
|
|
|
|
if( !hInst->custom_loader )
|
|
|
|
|
FreeLibrary( hInst->hInstance );
|
|
|
|
|
|
|
|
|
|
hInst->hInstance = NULL;
|
|
|
|
|
|
|
|
|
|
if( hInst->num_ordinals )
|
|
|
|
|
FreeNameFuncGlobals( hInst );
|
|
|
|
|
Mem_Free( hInst ); // done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dword Com_FunctionFromName( void *hInstance, const char *pName )
|
|
|
|
|
{
|
|
|
|
|
dll_user_t *hInst = (dll_user_t *)hInstance;
|
|
|
|
|
int i, index;
|
|
|
|
|
|
|
|
|
|
if( !hInst || !hInst->hInstance )
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
|
|
|
{
|
2011-03-09 22:00:00 +01:00
|
|
|
|
if( !Q_strcmp( pName, hInst->names[i] ))
|
2011-03-08 22:00:00 +01:00
|
|
|
|
{
|
|
|
|
|
index = hInst->ordinals[i];
|
|
|
|
|
return hInst->funcs[index] + hInst->funcBase;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// couldn't find the function name to return address
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *Com_NameForFunction( void *hInstance, dword function )
|
|
|
|
|
{
|
|
|
|
|
dll_user_t *hInst = (dll_user_t *)hInstance;
|
|
|
|
|
int i, index;
|
|
|
|
|
|
|
|
|
|
if( !hInst || !hInst->hInstance )
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
|
|
|
{
|
|
|
|
|
index = hInst->ordinals[i];
|
|
|
|
|
|
|
|
|
|
if(( function - hInst->funcBase ) == hInst->funcs[index] )
|
|
|
|
|
return hInst->names[i];
|
|
|
|
|
}
|
|
|
|
|
// couldn't find the function address to return name
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|