mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-15 09:30:01 +00:00
5e0a0765ce
The `.editorconfig` file in this repo is configured to trim all trailing whitespace regardless of whether the line is modified. Trims all trailing whitespace in the repository to make the codebase easier to work with in editors that respect `.editorconfig`. `git blame` becomes less useful on these lines but it already isn't very useful. Commands: ``` find . -type f -name '*.h' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ find . -type f -name '*.c' -exec sed --in-place 's/[[:space:]]\+$//' {} \+ ```
1152 lines
30 KiB
C
1152 lines
30 KiB
C
/*
|
|
library.c - custom dlls loader
|
|
Copyright (C) 2008 Uncle Mike
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
*/
|
|
#include "platform/platform.h"
|
|
#if XASH_LIB == LIB_WIN32
|
|
#include "common.h"
|
|
#include "library.h"
|
|
|
|
#ifdef XASH_64BIT
|
|
#include <dbghelp.h>
|
|
|
|
void *COM_LoadLibrary( const char *dllname, int build_ordinals_table )
|
|
{
|
|
return LoadLibraryA( dllname );
|
|
}
|
|
|
|
void COM_FreeLibrary( void *hInstance )
|
|
{
|
|
FreeLibrary( hInstance );
|
|
}
|
|
|
|
void *COM_GetProcAddress( void *hInstance, const char *name )
|
|
{
|
|
return GetProcAddress( hInstance, name );
|
|
}
|
|
|
|
void *COM_FunctionFromName( void *hInstance, const char *name )
|
|
{
|
|
return GetProcAddress( hInstance, name );
|
|
}
|
|
|
|
const char *COM_NameForFunction( void *hInstance, void *function )
|
|
{
|
|
#if 0
|
|
static qboolean initialized = false;
|
|
if( initialized )
|
|
{
|
|
char message[1024];
|
|
int len = 0;
|
|
size_t i;
|
|
HANDLE process = GetCurrentProcess();
|
|
HANDLE thread = GetCurrentThread();
|
|
IMAGEHLP_LINE64 line;
|
|
DWORD dline = 0;
|
|
DWORD options;
|
|
CONTEXT context;
|
|
STACKFRAME64 stackframe;
|
|
DWORD image;
|
|
char buffer[sizeof( IMAGEHLP_SYMBOL64) + MAX_SYM_NAME * sizeof(TCHAR)];
|
|
PIMAGEHLP_SYMBOL64 symbol = ( PIMAGEHLP_SYMBOL64)buffer;
|
|
memset( symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME );
|
|
symbol->SizeOfStruct = sizeof( IMAGEHLP_SYMBOL64);
|
|
symbol->MaxNameLength = MAX_SYM_NAME;
|
|
DWORD displacement = 0;
|
|
|
|
options = SymGetOptions();
|
|
SymSetOptions( options );
|
|
|
|
SymInitialize( process, NULL, TRUE );
|
|
|
|
if( SymGetSymFromAddr64( process, function, &displacement, symbol ) )
|
|
{
|
|
Msg( "%s\n", symbol->Name );
|
|
return copystring( symbol->Name );
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS
|
|
return COM_OffsetNameForFunction( function );
|
|
#endif
|
|
|
|
return NULL;
|
|
}
|
|
#else // XASH_64BIT
|
|
|
|
/*
|
|
---------------------------------------------------------------
|
|
|
|
Custom dlls loader
|
|
|
|
---------------------------------------------------------------
|
|
*/
|
|
|
|
#define DOS_SIGNATURE 0x5A4D // MZ
|
|
#define NT_SIGNATURE 0x00004550 // PE00
|
|
#define NUMBER_OF_DIRECTORY_ENTRIES 16
|
|
#ifndef IMAGE_SIZEOF_BASE_RELOCATION
|
|
#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION ))
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
// dos .exe header
|
|
word e_magic; // magic number
|
|
word e_cblp; // bytes on last page of file
|
|
word e_cp; // pages in file
|
|
word e_crlc; // relocations
|
|
word e_cparhdr; // size of header in paragraphs
|
|
word e_minalloc; // minimum extra paragraphs needed
|
|
word e_maxalloc; // maximum extra paragraphs needed
|
|
word e_ss; // initial (relative) SS value
|
|
word e_sp; // initial SP value
|
|
word e_csum; // checksum
|
|
word e_ip; // initial IP value
|
|
word e_cs; // initial (relative) CS value
|
|
word e_lfarlc; // file address of relocation table
|
|
word e_ovno; // overlay number
|
|
word e_res[4]; // reserved words
|
|
word e_oemid; // OEM identifier (for e_oeminfo)
|
|
word e_oeminfo; // OEM information; e_oemid specific
|
|
word e_res2[10]; // reserved words
|
|
long e_lfanew; // file address of new exe header
|
|
} DOS_HEADER;
|
|
|
|
typedef struct
|
|
{
|
|
// win .exe header
|
|
word Machine;
|
|
word NumberOfSections;
|
|
dword TimeDateStamp;
|
|
dword PointerToSymbolTable;
|
|
dword NumberOfSymbols;
|
|
word SizeOfOptionalHeader;
|
|
word Characteristics;
|
|
} PE_HEADER;
|
|
|
|
typedef struct
|
|
{
|
|
dword VirtualAddress;
|
|
dword Size;
|
|
} DATA_DIRECTORY;
|
|
|
|
typedef struct
|
|
{
|
|
word Magic;
|
|
byte MajorLinkerVersion;
|
|
byte MinorLinkerVersion;
|
|
dword SizeOfCode;
|
|
dword SizeOfInitializedData;
|
|
dword SizeOfUninitializedData;
|
|
dword AddressOfEntryPoint;
|
|
dword BaseOfCode;
|
|
dword BaseOfData;
|
|
dword ImageBase;
|
|
dword SectionAlignment;
|
|
dword FileAlignment;
|
|
word MajorOperatingSystemVersion;
|
|
word MinorOperatingSystemVersion;
|
|
word MajorImageVersion;
|
|
word MinorImageVersion;
|
|
word MajorSubsystemVersion;
|
|
word MinorSubsystemVersion;
|
|
dword Win32VersionValue;
|
|
dword SizeOfImage;
|
|
dword SizeOfHeaders;
|
|
dword CheckSum;
|
|
word Subsystem;
|
|
word DllCharacteristics;
|
|
dword SizeOfStackReserve;
|
|
dword SizeOfStackCommit;
|
|
dword SizeOfHeapReserve;
|
|
dword SizeOfHeapCommit;
|
|
dword LoaderFlags;
|
|
dword NumberOfRvaAndSizes;
|
|
|
|
DATA_DIRECTORY DataDirectory[NUMBER_OF_DIRECTORY_ENTRIES];
|
|
} OPTIONAL_HEADER;
|
|
|
|
typedef struct
|
|
{
|
|
dword Characteristics;
|
|
dword TimeDateStamp;
|
|
word MajorVersion;
|
|
word MinorVersion;
|
|
dword Name;
|
|
dword Base;
|
|
dword NumberOfFunctions;
|
|
dword NumberOfNames;
|
|
dword AddressOfFunctions; // RVA from base of image
|
|
dword AddressOfNames; // RVA from base of image
|
|
dword AddressOfNameOrdinals; // RVA from base of image
|
|
} EXPORT_DIRECTORY;
|
|
|
|
typedef struct
|
|
{
|
|
PIMAGE_NT_HEADERS headers;
|
|
byte *codeBase;
|
|
void **modules;
|
|
int numModules;
|
|
int initialized;
|
|
} MEMORYMODULE, *PMEMORYMODULE;
|
|
|
|
typedef struct
|
|
{
|
|
byte Name[8]; // dos name length
|
|
|
|
union
|
|
{
|
|
dword PhysicalAddress;
|
|
dword VirtualSize;
|
|
} Misc;
|
|
|
|
dword VirtualAddress;
|
|
dword SizeOfRawData;
|
|
dword PointerToRawData;
|
|
dword PointerToRelocations;
|
|
dword PointerToLinenumbers;
|
|
word NumberOfRelocations;
|
|
word NumberOfLinenumbers;
|
|
dword Characteristics;
|
|
} SECTION_HEADER;
|
|
|
|
// Protection flags for memory pages (Executable, Readable, Writeable)
|
|
static int ProtectionFlags[2][2][2] =
|
|
{
|
|
{
|
|
{ PAGE_NOACCESS, PAGE_WRITECOPY }, // not executable
|
|
{ PAGE_READONLY, PAGE_READWRITE },
|
|
},
|
|
{
|
|
{ PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY }, // executable
|
|
{ PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE },
|
|
},
|
|
};
|
|
|
|
typedef BOOL (WINAPI *DllEntryProc)( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved );
|
|
|
|
#define GET_HEADER_DICTIONARY( module, idx ) &(module)->headers->OptionalHeader.DataDirectory[idx]
|
|
#define CALCULATE_ADDRESS( base, offset ) ((DWORD)(base) + (DWORD)(offset))
|
|
|
|
static void CopySections( const byte *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
|
|
{
|
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers );
|
|
byte *codeBase = module->codeBase;
|
|
int i, size;
|
|
byte *dest;
|
|
|
|
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
|
|
{
|
|
if( section->SizeOfRawData == 0 )
|
|
{
|
|
// section doesn't contain data in the dll itself, but may define
|
|
// uninitialized data
|
|
size = old_headers->OptionalHeader.SectionAlignment;
|
|
|
|
if( size > 0 )
|
|
{
|
|
dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), size, MEM_COMMIT, PAGE_READWRITE );
|
|
section->Misc.PhysicalAddress = (DWORD)dest;
|
|
memset( dest, 0, size );
|
|
}
|
|
// section is empty
|
|
continue;
|
|
}
|
|
|
|
// commit memory block and copy data from dll
|
|
dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), section->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE );
|
|
memcpy( dest, (byte *)CALCULATE_ADDRESS(data, section->PointerToRawData), section->SizeOfRawData );
|
|
section->Misc.PhysicalAddress = (DWORD)dest;
|
|
}
|
|
}
|
|
|
|
static void FreeSections( PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module )
|
|
{
|
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers);
|
|
byte *codeBase = module->codeBase;
|
|
int i, size;
|
|
|
|
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
|
|
{
|
|
if( section->SizeOfRawData == 0 )
|
|
{
|
|
size = old_headers->OptionalHeader.SectionAlignment;
|
|
if( size > 0 )
|
|
{
|
|
VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), size, MEM_DECOMMIT );
|
|
section->Misc.PhysicalAddress = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), section->SizeOfRawData, MEM_DECOMMIT );
|
|
section->Misc.PhysicalAddress = 0;
|
|
}
|
|
}
|
|
|
|
static void FinalizeSections( MEMORYMODULE *module )
|
|
{
|
|
PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers );
|
|
int i;
|
|
|
|
// loop through all sections and change access flags
|
|
for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ )
|
|
{
|
|
DWORD protect, oldProtect, size;
|
|
int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
|
int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0;
|
|
int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0;
|
|
|
|
if( section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE )
|
|
{
|
|
// section is not needed any more and can safely be freed
|
|
VirtualFree((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, MEM_DECOMMIT);
|
|
continue;
|
|
}
|
|
|
|
// determine protection flags based on characteristics
|
|
protect = ProtectionFlags[executable][readable][writeable];
|
|
if( section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED )
|
|
protect |= PAGE_NOCACHE;
|
|
|
|
// determine size of region
|
|
size = section->SizeOfRawData;
|
|
|
|
if( size == 0 )
|
|
{
|
|
if( section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA )
|
|
size = module->headers->OptionalHeader.SizeOfInitializedData;
|
|
else if( section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA )
|
|
size = module->headers->OptionalHeader.SizeOfUninitializedData;
|
|
}
|
|
|
|
if( size > 0 )
|
|
{
|
|
// change memory access flags
|
|
if( !VirtualProtect((LPVOID)section->Misc.PhysicalAddress, size, protect, &oldProtect ))
|
|
Sys_Error( "error protecting memory page\n" );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta )
|
|
{
|
|
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_BASERELOC );
|
|
byte *codeBase = module->codeBase;
|
|
DWORD i;
|
|
|
|
if( directory->Size > 0 )
|
|
{
|
|
PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress );
|
|
for( ; relocation->VirtualAddress > 0; )
|
|
{
|
|
byte *dest = (byte *)CALCULATE_ADDRESS( codeBase, relocation->VirtualAddress );
|
|
word *relInfo = (word *)((byte *)relocation + IMAGE_SIZEOF_BASE_RELOCATION );
|
|
|
|
for( i = 0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++ )
|
|
{
|
|
DWORD *patchAddrHL;
|
|
int type, offset;
|
|
|
|
// the upper 4 bits define the type of relocation
|
|
type = *relInfo >> 12;
|
|
// the lower 12 bits define the offset
|
|
offset = *relInfo & 0xfff;
|
|
|
|
switch( type )
|
|
{
|
|
case IMAGE_REL_BASED_ABSOLUTE:
|
|
// skip relocation
|
|
break;
|
|
case IMAGE_REL_BASED_HIGHLOW:
|
|
// change complete 32 bit address
|
|
patchAddrHL = (DWORD *)CALCULATE_ADDRESS( dest, offset );
|
|
*patchAddrHL += delta;
|
|
break;
|
|
default:
|
|
Con_Reportf( S_ERROR "PerformBaseRelocation: unknown relocation: %d\n", type );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// advance to next relocation block
|
|
relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( relocation, relocation->SizeOfBlock );
|
|
}
|
|
}
|
|
}
|
|
|
|
static FARPROC MemoryGetProcAddress( void *module, const char *name )
|
|
{
|
|
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((MEMORYMODULE *)module, IMAGE_DIRECTORY_ENTRY_EXPORT );
|
|
byte *codeBase = ((PMEMORYMODULE)module)->codeBase;
|
|
PIMAGE_EXPORT_DIRECTORY exports;
|
|
int idx = -1;
|
|
DWORD i, *nameRef;
|
|
WORD *ordinal;
|
|
|
|
if( directory->Size == 0 )
|
|
{
|
|
// no export table found
|
|
return NULL;
|
|
}
|
|
|
|
exports = (PIMAGE_EXPORT_DIRECTORY)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress );
|
|
|
|
if( exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0 )
|
|
{
|
|
// DLL doesn't export anything
|
|
return NULL;
|
|
}
|
|
|
|
// search function name in list of exported names
|
|
nameRef = (DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNames );
|
|
ordinal = (WORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNameOrdinals );
|
|
|
|
for( i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++ )
|
|
{
|
|
// GetProcAddress case insensative ?????
|
|
if( !Q_stricmp( name, (const char *)CALCULATE_ADDRESS( codeBase, *nameRef )))
|
|
{
|
|
idx = *ordinal;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( idx == -1 )
|
|
{
|
|
// exported symbol not found
|
|
return NULL;
|
|
}
|
|
|
|
if((DWORD)idx > exports->NumberOfFunctions )
|
|
{
|
|
// name <-> ordinal number don't match
|
|
return NULL;
|
|
}
|
|
|
|
// addressOfFunctions contains the RVAs to the "real" functions
|
|
return (FARPROC)CALCULATE_ADDRESS( codeBase, *(DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfFunctions + (idx * 4)));
|
|
}
|
|
|
|
static int BuildImportTable( MEMORYMODULE *module )
|
|
{
|
|
PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_IMPORT );
|
|
byte *codeBase = module->codeBase;
|
|
int result = 1;
|
|
|
|
if( directory->Size > 0 )
|
|
{
|
|
PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress );
|
|
|
|
for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR )) && importDesc->Name; importDesc++ )
|
|
{
|
|
DWORD *thunkRef, *funcRef;
|
|
LPCSTR libname;
|
|
void *handle;
|
|
|
|
libname = (LPCSTR)CALCULATE_ADDRESS( codeBase, importDesc->Name );
|
|
handle = COM_LoadLibrary( libname, false, true );
|
|
|
|
if( handle == NULL )
|
|
{
|
|
Con_Printf( S_ERROR "couldn't load library %s\n", libname );
|
|
result = 0;
|
|
break;
|
|
}
|
|
|
|
module->modules = (void *)Mem_Realloc( host.mempool, module->modules, (module->numModules + 1) * (sizeof( void* )));
|
|
module->modules[module->numModules++] = handle;
|
|
|
|
if( importDesc->OriginalFirstThunk )
|
|
{
|
|
thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->OriginalFirstThunk );
|
|
funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk );
|
|
}
|
|
else
|
|
{
|
|
// no hint table
|
|
thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk );
|
|
funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk );
|
|
}
|
|
|
|
for( ; *thunkRef; thunkRef++, funcRef++ )
|
|
{
|
|
LPCSTR funcName;
|
|
|
|
if( IMAGE_SNAP_BY_ORDINAL( *thunkRef ))
|
|
{
|
|
funcName = (LPCSTR)IMAGE_ORDINAL( *thunkRef );
|
|
*funcRef = (DWORD)COM_GetProcAddress( handle, funcName );
|
|
}
|
|
else
|
|
{
|
|
PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS( codeBase, *thunkRef );
|
|
funcName = (LPCSTR)&thunkData->Name;
|
|
*funcRef = (DWORD)COM_GetProcAddress( handle, funcName );
|
|
}
|
|
|
|
if( *funcRef == 0 )
|
|
{
|
|
Con_Printf( S_ERROR "%s unable to find address: %s\n", libname, funcName );
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
if( !result ) break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void MemoryFreeLibrary( void *hInstance )
|
|
{
|
|
MEMORYMODULE *module = (MEMORYMODULE *)hInstance;
|
|
|
|
if( module != NULL )
|
|
{
|
|
int i;
|
|
|
|
if( module->initialized != 0 )
|
|
{
|
|
// notify library about detaching from process
|
|
DllEntryProc DllEntry = (DllEntryProc)CALCULATE_ADDRESS( module->codeBase, module->headers->OptionalHeader.AddressOfEntryPoint );
|
|
(*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0 );
|
|
module->initialized = 0;
|
|
}
|
|
|
|
if( module->modules != NULL )
|
|
{
|
|
// free previously opened libraries
|
|
for( i = 0; i < module->numModules; i++ )
|
|
{
|
|
if( module->modules[i] != NULL )
|
|
COM_FreeLibrary( module->modules[i] );
|
|
}
|
|
Mem_Free( module->modules ); // Mem_Realloc end
|
|
}
|
|
|
|
FreeSections( module->headers, module );
|
|
|
|
if( module->codeBase != NULL )
|
|
{
|
|
// release memory of library
|
|
VirtualFree( module->codeBase, 0, MEM_RELEASE );
|
|
}
|
|
|
|
HeapFree( GetProcessHeap(), 0, module );
|
|
}
|
|
}
|
|
|
|
void *MemoryLoadLibrary( const char *name )
|
|
{
|
|
MEMORYMODULE *result = NULL;
|
|
PIMAGE_DOS_HEADER dos_header;
|
|
PIMAGE_NT_HEADERS old_header;
|
|
byte *code, *headers;
|
|
DWORD locationDelta;
|
|
DllEntryProc DllEntry;
|
|
string errorstring;
|
|
qboolean successfull;
|
|
void *data = NULL;
|
|
|
|
data = FS_LoadFile( name, NULL, false );
|
|
|
|
if( !data )
|
|
{
|
|
Q_sprintf( errorstring, "couldn't load %s", name );
|
|
goto library_error;
|
|
}
|
|
|
|
dos_header = (PIMAGE_DOS_HEADER)data;
|
|
if( dos_header->e_magic != IMAGE_DOS_SIGNATURE )
|
|
{
|
|
Q_sprintf( errorstring, "%s it's not a valid executable file", name );
|
|
goto library_error;
|
|
}
|
|
|
|
old_header = (PIMAGE_NT_HEADERS)&((const byte *)(data))[dos_header->e_lfanew];
|
|
if( old_header->Signature != IMAGE_NT_SIGNATURE )
|
|
{
|
|
Q_sprintf( errorstring, "%s missing PE header", name );
|
|
goto library_error;
|
|
}
|
|
|
|
// reserve memory for image of library
|
|
code = (byte *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE );
|
|
|
|
if( code == NULL )
|
|
{
|
|
// try to allocate memory at arbitrary position
|
|
code = (byte *)VirtualAlloc( NULL, old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE );
|
|
}
|
|
|
|
if( code == NULL )
|
|
{
|
|
Q_sprintf( errorstring, "%s can't reserve memory", name );
|
|
goto library_error;
|
|
}
|
|
|
|
result = (MEMORYMODULE *)HeapAlloc( GetProcessHeap(), 0, sizeof( MEMORYMODULE ));
|
|
result->codeBase = code;
|
|
result->numModules = 0;
|
|
result->modules = NULL;
|
|
result->initialized = 0;
|
|
|
|
// XXX: is it correct to commit the complete memory region at once?
|
|
// calling DllEntry raises an exception if we don't...
|
|
VirtualAlloc( code, old_header->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE );
|
|
|
|
// commit memory for headers
|
|
headers = (byte *)VirtualAlloc( code, old_header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE );
|
|
|
|
// copy PE header to code
|
|
memcpy( headers, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders );
|
|
result->headers = (PIMAGE_NT_HEADERS)&((const byte *)(headers))[dos_header->e_lfanew];
|
|
|
|
// update position
|
|
result->headers->OptionalHeader.ImageBase = (DWORD)code;
|
|
|
|
// copy sections from DLL file block to new memory location
|
|
CopySections( data, old_header, result );
|
|
|
|
// adjust base address of imported data
|
|
locationDelta = (DWORD)(code - old_header->OptionalHeader.ImageBase);
|
|
if( locationDelta != 0 ) PerformBaseRelocation( result, locationDelta );
|
|
|
|
// load required dlls and adjust function table of imports
|
|
if( !BuildImportTable( result ))
|
|
{
|
|
Q_sprintf( errorstring, "%s failed to build import table", name );
|
|
goto library_error;
|
|
}
|
|
|
|
// mark memory pages depending on section headers and release
|
|
// sections that are marked as "discardable"
|
|
FinalizeSections( result );
|
|
|
|
// get entry point of loaded library
|
|
if( result->headers->OptionalHeader.AddressOfEntryPoint != 0 )
|
|
{
|
|
DllEntry = (DllEntryProc)CALCULATE_ADDRESS( code, result->headers->OptionalHeader.AddressOfEntryPoint );
|
|
if( DllEntry == 0 )
|
|
{
|
|
Q_sprintf( errorstring, "%s has no entry point", name );
|
|
goto library_error;
|
|
}
|
|
|
|
// notify library about attaching to process
|
|
successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0 );
|
|
if( !successfull )
|
|
{
|
|
Q_sprintf( errorstring, "can't attach library %s", name );
|
|
goto library_error;
|
|
}
|
|
result->initialized = 1;
|
|
}
|
|
|
|
Mem_Free( data ); // release memory
|
|
return (void *)result;
|
|
library_error:
|
|
// cleanup
|
|
if( data ) Mem_Free( data );
|
|
MemoryFreeLibrary( result );
|
|
Con_Printf( S_ERROR "LoadLibrary: %s\n", errorstring );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
---------------------------------------------------------------
|
|
|
|
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 )
|
|
{
|
|
static string out_name;
|
|
char *pos;
|
|
|
|
if( in_name[0] == '?' ) // is this a MSVC C++ mangled name?
|
|
{
|
|
if(( pos = Q_strstr( in_name, "@@" )) != NULL )
|
|
{
|
|
int len = pos - in_name;
|
|
|
|
// strip off the leading '?'
|
|
Q_strncpy( out_name, in_name + 1, sizeof( out_name ));
|
|
out_name[len-1] = 0; // terminate string at the "@@"
|
|
return out_name;
|
|
}
|
|
}
|
|
|
|
Q_strncpy( out_name, in_name, sizeof( out_name ));
|
|
|
|
return out_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 rdata_found;
|
|
OPTIONAL_HEADER optional_header;
|
|
long rdata_delta = 0;
|
|
EXPORT_DIRECTORY export_directory;
|
|
long name_offset;
|
|
long exports_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 )
|
|
{
|
|
Q_sprintf( errorstring, "couldn't load %s", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( FS_Read( f, &dos_header, sizeof( dos_header )) != sizeof( dos_header ))
|
|
{
|
|
Q_sprintf( errorstring, "%s has corrupted EXE header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( dos_header.e_magic != DOS_SIGNATURE )
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid dll signature", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( FS_Seek( f, dos_header.e_lfanew, SEEK_SET ) == -1 )
|
|
{
|
|
Q_sprintf( errorstring, "%s error seeking for new exe header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( FS_Read( f, &nt_signature, sizeof( nt_signature )) != sizeof( nt_signature ))
|
|
{
|
|
Q_sprintf( errorstring, "%s has corrupted NT header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( nt_signature != NT_SIGNATURE )
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid NT signature", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( FS_Read( f, &pe_header, sizeof( pe_header )) != sizeof( pe_header ))
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid PE header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( !pe_header.SizeOfOptionalHeader )
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have an optional header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( FS_Read( f, &optional_header, sizeof( optional_header )) != sizeof( optional_header ))
|
|
{
|
|
Q_sprintf( errorstring, "%s optional header probably corrupted", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
rdata_found = false;
|
|
|
|
for( i = 0; i < pe_header.NumberOfSections; i++ )
|
|
{
|
|
if( FS_Read( f, §ion_header, sizeof( section_header )) != sizeof( section_header ))
|
|
{
|
|
Q_sprintf( errorstring, "%s error during reading section header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if((( optional_header.DataDirectory[0].VirtualAddress >= section_header.VirtualAddress ) &&
|
|
(optional_header.DataDirectory[0].VirtualAddress < (section_header.VirtualAddress + section_header.Misc.VirtualSize))))
|
|
{
|
|
rdata_found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( rdata_found )
|
|
{
|
|
rdata_delta = section_header.VirtualAddress - section_header.PointerToRawData;
|
|
}
|
|
|
|
exports_offset = optional_header.DataDirectory[0].VirtualAddress - rdata_delta;
|
|
|
|
if( FS_Seek( f, exports_offset, SEEK_SET ) == -1 )
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid exports section", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
if( FS_Read( f, &export_directory, sizeof( export_directory )) != sizeof( export_directory ))
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid optional header", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
hInst->num_ordinals = export_directory.NumberOfNames; // also number of ordinals
|
|
|
|
if( hInst->num_ordinals > MAX_LIBRARY_EXPORTS )
|
|
{
|
|
Q_sprintf( errorstring, "%s too many exports %i", hInst->shortPath, hInst->num_ordinals );
|
|
hInst->num_ordinals = 0;
|
|
goto table_error;
|
|
}
|
|
|
|
ordinal_offset = export_directory.AddressOfNameOrdinals - rdata_delta;
|
|
|
|
if( FS_Seek( f, ordinal_offset, SEEK_SET ) == -1 )
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid ordinals section", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
hInst->ordinals = Mem_Malloc( host.mempool, hInst->num_ordinals * sizeof( word ));
|
|
|
|
if( FS_Read( f, hInst->ordinals, hInst->num_ordinals * sizeof( word )) != (hInst->num_ordinals * sizeof( word )))
|
|
{
|
|
Q_sprintf( errorstring, "%s error during reading ordinals table", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
function_offset = export_directory.AddressOfFunctions - rdata_delta;
|
|
|
|
if( FS_Seek( f, function_offset, SEEK_SET ) == -1 )
|
|
{
|
|
Q_sprintf( errorstring, "%s does not have a valid export address section", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
hInst->funcs = Mem_Malloc( host.mempool, hInst->num_ordinals * sizeof( dword ));
|
|
|
|
if( FS_Read( f, hInst->funcs, hInst->num_ordinals * sizeof( dword )) != (hInst->num_ordinals * sizeof( dword )))
|
|
{
|
|
Q_sprintf( errorstring, "%s error during reading export address section", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
name_offset = export_directory.AddressOfNames - rdata_delta;
|
|
|
|
if( FS_Seek( f, name_offset, SEEK_SET ) == -1 )
|
|
{
|
|
Q_sprintf( errorstring, "%s file does not have a valid names section", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
p_Names = Mem_Malloc( host.mempool, hInst->num_ordinals * sizeof( dword ));
|
|
|
|
if( FS_Read( f, p_Names, hInst->num_ordinals * sizeof( dword )) != (hInst->num_ordinals * sizeof( dword )))
|
|
{
|
|
Q_sprintf( errorstring, "%s error during reading names table", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
{
|
|
name_offset = p_Names[i] - rdata_delta;
|
|
|
|
if( name_offset != 0 )
|
|
{
|
|
if( FS_Seek( f, name_offset, SEEK_SET ) != -1 )
|
|
{
|
|
FsGetString( f, function_name );
|
|
hInst->names[i] = copystring( GetMSVCName( function_name ));
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
|
|
if( i != hInst->num_ordinals )
|
|
{
|
|
Q_sprintf( errorstring, "%s error during loading names section", hInst->shortPath );
|
|
goto table_error;
|
|
}
|
|
FS_Close( f );
|
|
|
|
for( i = 0; i < hInst->num_ordinals; i++ )
|
|
{
|
|
if( !Q_strcmp( "GiveFnptrsToDll", hInst->names[i] )) // main entry point for user dlls
|
|
{
|
|
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 );
|
|
Con_Printf( S_ERROR "LoadLibrary: %s\n", errorstring );
|
|
|
|
return false;
|
|
}
|
|
|
|
qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname, qboolean directpath )
|
|
{
|
|
PIMAGE_DOS_HEADER dos_header;
|
|
PIMAGE_NT_HEADERS old_header;
|
|
PIMAGE_DATA_DIRECTORY directory;
|
|
PIMAGE_IMPORT_DESCRIPTOR importDesc;
|
|
string errorstring = "";
|
|
void *data = NULL;
|
|
dll_user_t *hInst;
|
|
|
|
hInst = FS_FindLibrary( name, directpath );
|
|
if( !hInst )
|
|
{
|
|
return false; // nothing to load
|
|
}
|
|
|
|
data = FS_LoadFile( name, NULL, false );
|
|
if( !data )
|
|
{
|
|
Q_snprintf( errorstring, sizeof( errorstring ), "couldn't load %s", name );
|
|
goto libraryerror;
|
|
}
|
|
|
|
dos_header = ( PIMAGE_DOS_HEADER )data;
|
|
if( dos_header->e_magic != IMAGE_DOS_SIGNATURE )
|
|
{
|
|
Q_snprintf( errorstring, sizeof( errorstring ), "%s it's not a valid executable file", name );
|
|
goto libraryerror;
|
|
}
|
|
|
|
old_header = ( PIMAGE_NT_HEADERS )&( ( const byte * )( data ) )[dos_header->e_lfanew];
|
|
if( old_header->Signature != IMAGE_NT_SIGNATURE )
|
|
{
|
|
Q_snprintf( errorstring, sizeof( errorstring ), "%s missing PE header", name );
|
|
goto libraryerror;
|
|
}
|
|
|
|
directory = &old_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
|
|
|
if( directory->Size <= 0 )
|
|
{
|
|
Q_snprintf( errorstring, sizeof( errorstring ), "%s has no dependencies. Is this dll valid?\n", name );
|
|
goto libraryerror;
|
|
}
|
|
|
|
importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( data, directory->VirtualAddress );
|
|
|
|
for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++ )
|
|
{
|
|
const char *importName = ( const char* )CALCULATE_ADDRESS( data, importDesc->Name );
|
|
Con_Reportf( "library %s has direct dependency %s\n", name, importName );
|
|
|
|
if( !Q_stricmp( importName, depname ) )
|
|
{
|
|
Mem_Free( data );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
libraryerror:
|
|
if( errorstring[0] ) Con_Printf( errorstring );
|
|
if( data ) Mem_Free( data ); // release memory
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
================
|
|
COM_LoadLibrary
|
|
|
|
smart dll loader - can loading dlls from pack or wad files
|
|
================
|
|
*/
|
|
void *COM_LoadLibrary( 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 )
|
|
{
|
|
Con_Printf( S_ERROR "LoadLibrary: couldn't load encrypted library %s\n", dllname );
|
|
return NULL;
|
|
}
|
|
|
|
hInst->hInstance = MemoryLoadLibrary( hInst->fullPath );
|
|
}
|
|
else hInst->hInstance = LoadLibrary( hInst->fullPath );
|
|
|
|
if( !hInst->hInstance )
|
|
{
|
|
Con_Reportf( "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 ))
|
|
{
|
|
Con_Reportf( "LoadLibrary: Loading %s - failed\n", dllname );
|
|
COM_FreeLibrary( hInst );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
Con_Reportf( "LoadLibrary: Loading %s - ok\n", dllname );
|
|
|
|
return hInst;
|
|
}
|
|
|
|
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 (void *)MemoryGetProcAddress( hInst->hInstance, name );
|
|
return (void *)GetProcAddress( hInst->hInstance, name );
|
|
}
|
|
|
|
void COM_FreeLibrary( void *hInstance )
|
|
{
|
|
dll_user_t *hInst = (dll_user_t *)hInstance;
|
|
|
|
if( !hInst || !hInst->hInstance )
|
|
return; // already freed
|
|
|
|
if( host.status == HOST_CRASHED )
|
|
{
|
|
// we need to hold down all modules, while MSVC can find error
|
|
Con_Reportf( "Sys_FreeLibrary: hold %s for debugging\n", hInst->dllName );
|
|
return;
|
|
}
|
|
else Con_Reportf( "Sys_FreeLibrary: Unloading %s\n", hInst->dllName );
|
|
|
|
if( hInst->custom_loader )
|
|
MemoryFreeLibrary( hInst->hInstance );
|
|
else FreeLibrary( hInst->hInstance );
|
|
|
|
hInst->hInstance = NULL;
|
|
|
|
if( hInst->num_ordinals )
|
|
FreeNameFuncGlobals( hInst );
|
|
Mem_Free( hInst ); // done
|
|
}
|
|
|
|
void *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++ )
|
|
{
|
|
if( !Q_strcmp( pName, hInst->names[i] ))
|
|
{
|
|
index = hInst->ordinals[i];
|
|
return hInst->funcs[index] + hInst->funcBase;
|
|
}
|
|
}
|
|
|
|
// couldn't find the function name to return address
|
|
Con_Printf( "Can't find proc: %s\n", pName );
|
|
|
|
return 0;
|
|
}
|
|
|
|
const char *COM_NameForFunction( void *hInstance, void *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(( (char*)function - (char*)hInst->funcBase ) == hInst->funcs[index] )
|
|
return hInst->names[i];
|
|
}
|
|
|
|
// couldn't find the function address to return name
|
|
Con_Printf( "Can't find address: %08lx\n", function );
|
|
|
|
return NULL;
|
|
}
|
|
#endif // XASH_64BIT
|
|
#endif // _WIN32
|