mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-17 10:30:00 +00:00
platform: win32: improve error reporting when loading DLLs and move custom DLL loader to a separate file
This commit is contained in:
parent
f467d0c807
commit
4c7bf1ff44
478
engine/platform/win32/lib_custom_win.c
Normal file
478
engine/platform/win32/lib_custom_win.c
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
lib_custom_win.c - win32 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 "common.h"
|
||||
|
||||
#if XASH_LIB == LIB_WIN32 && XASH_X86
|
||||
#include "lib_win.h"
|
||||
|
||||
#define NUMBER_OF_DIRECTORY_ENTRIES 16
|
||||
#ifndef IMAGE_SIZEOF_BASE_RELOCATION
|
||||
#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION ))
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PIMAGE_NT_HEADERS headers;
|
||||
byte *codeBase;
|
||||
void **modules;
|
||||
int numModules;
|
||||
int initialized;
|
||||
} MEMORYMODULE, *PMEMORYMODULE;
|
||||
|
||||
// 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]
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#endif // XASH_LIB == LIB_WIN32 && XASH_X86
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
library.c - custom dlls loader
|
||||
lin_win.c - win32 dynamic library loading
|
||||
Copyright (C) 2008 Uncle Mike
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -12,481 +12,11 @@ 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"
|
||||
#include <winnt.h>
|
||||
|
||||
#define CALCULATE_ADDRESS( base, offset ) ( ( DWORD )( base ) + ( DWORD )( offset ) )
|
||||
|
||||
#if XASH_X86
|
||||
/*
|
||||
---------------------------------------------------------------
|
||||
|
||||
Custom dlls loader
|
||||
|
||||
---------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define NUMBER_OF_DIRECTORY_ENTRIES 16
|
||||
#ifndef IMAGE_SIZEOF_BASE_RELOCATION
|
||||
#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION ))
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PIMAGE_NT_HEADERS headers;
|
||||
byte *codeBase;
|
||||
void **modules;
|
||||
int numModules;
|
||||
int initialized;
|
||||
} MEMORYMODULE, *PMEMORYMODULE;
|
||||
|
||||
// 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]
|
||||
|
||||
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;
|
||||
}
|
||||
#endif
|
||||
#if XASH_LIB == LIB_WIN32
|
||||
#include "lib_win.h"
|
||||
|
||||
static DWORD GetOffsetByRVA( DWORD rva, PIMAGE_NT_HEADERS nt_header )
|
||||
{
|
||||
@ -764,70 +294,146 @@ table_error:
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname, qboolean directpath )
|
||||
static const char *GetLastErrorAsString( void )
|
||||
{
|
||||
PIMAGE_DOS_HEADER dosHeader;
|
||||
PIMAGE_NT_HEADERS peHeader;
|
||||
PIMAGE_DATA_DIRECTORY importDir;
|
||||
DWORD errorcode;
|
||||
static string errormessage;
|
||||
|
||||
errorcode = GetLastError();
|
||||
if ( !errorcode ) return "";
|
||||
|
||||
FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
||||
NULL, errorcode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
|
||||
(LPSTR)&errormessage, sizeof( errormessage ), NULL );
|
||||
|
||||
return errormessage;
|
||||
}
|
||||
|
||||
static PIMAGE_IMPORT_DESCRIPTOR GetImportDescriptor( const char *name, byte *data, PIMAGE_NT_HEADERS *peheader )
|
||||
{
|
||||
PIMAGE_DOS_HEADER dosHeader;
|
||||
PIMAGE_NT_HEADERS peHeader;
|
||||
PIMAGE_DATA_DIRECTORY importDir;
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDesc;
|
||||
string errorstring = "";
|
||||
byte *data = NULL;
|
||||
dll_user_t *hInst;
|
||||
|
||||
hInst = FS_FindLibrary( name, directpath );
|
||||
if( !hInst )
|
||||
if ( !data )
|
||||
{
|
||||
return false; // nothing to load
|
||||
Con_Printf( S_ERROR "%s: couldn't load %s\n", __FUNCTION__, name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = FS_LoadFile( name, NULL, false );
|
||||
if( !data )
|
||||
dosHeader = (PIMAGE_DOS_HEADER)data;
|
||||
if ( dosHeader->e_magic != IMAGE_DOS_SIGNATURE )
|
||||
{
|
||||
Q_snprintf( errorstring, sizeof( errorstring ), "couldn't load %s", name );
|
||||
goto libraryerror;
|
||||
Con_Printf( S_ERROR "%s: %s is not a valid executable file\n", __FUNCTION__, name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dosHeader = ( PIMAGE_DOS_HEADER )data;
|
||||
if( dosHeader->e_magic != IMAGE_DOS_SIGNATURE )
|
||||
peHeader = (PIMAGE_NT_HEADERS)( data + dosHeader->e_lfanew );
|
||||
if ( peHeader->Signature != IMAGE_NT_SIGNATURE )
|
||||
{
|
||||
Q_snprintf( errorstring, sizeof( errorstring ), "%s it's not a valid executable file", name );
|
||||
goto libraryerror;
|
||||
}
|
||||
|
||||
peHeader = ( PIMAGE_NT_HEADERS )(data + dosHeader->e_lfanew);
|
||||
if( peHeader->Signature != IMAGE_NT_SIGNATURE )
|
||||
{
|
||||
Q_snprintf( errorstring, sizeof( errorstring ), "%s missing PE header", name );
|
||||
goto libraryerror;
|
||||
Con_Printf( S_ERROR "%s: %s is missing a PE header\n", __FUNCTION__, name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
importDir = &peHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
if( importDir->Size <= 0 )
|
||||
{
|
||||
Con_Printf( S_WARN "%s: %s has no dependencies. Is this library valid?\n", __FUNCTION__, name );
|
||||
goto libraryerror;
|
||||
Con_Printf( S_ERROR "%s: %s has no dependencies\n", __FUNCTION__, name );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( data, GetOffsetByRVA(importDir->VirtualAddress, peHeader) );
|
||||
for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++ )
|
||||
*peheader = peHeader;
|
||||
importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( data, GetOffsetByRVA( importDir->VirtualAddress, peHeader ) );
|
||||
|
||||
return importDesc;
|
||||
}
|
||||
|
||||
static void ListMissingModules( dll_user_t *hInst )
|
||||
{
|
||||
DWORD cbNeeded;
|
||||
PIMAGE_NT_HEADERS peHeader;
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDesc;
|
||||
HANDLE hProcess;
|
||||
byte *data;
|
||||
|
||||
if ( !hInst ) return;
|
||||
|
||||
hProcess = GetCurrentProcess();
|
||||
|
||||
data = FS_LoadFile( hInst->dllName, NULL, false );
|
||||
if ( !data )
|
||||
{
|
||||
CloseHandle( hProcess );
|
||||
return;
|
||||
}
|
||||
|
||||
importDesc = GetImportDescriptor( hInst->dllName, data, &peHeader );
|
||||
if ( !importDesc )
|
||||
{
|
||||
CloseHandle( hProcess );
|
||||
Mem_Free( data );
|
||||
return;
|
||||
}
|
||||
|
||||
for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR ) ) && importDesc->Name; importDesc++ )
|
||||
{
|
||||
HMODULE hMod;
|
||||
const char *importName = (const char *)CALCULATE_ADDRESS( data, GetOffsetByRVA( importDesc->Name, peHeader ) );
|
||||
|
||||
hMod = LoadLibraryEx( importName, NULL, LOAD_LIBRARY_AS_DATAFILE );
|
||||
if ( !hMod )
|
||||
COM_PushLibraryError( va( "%s not found!", importName ) );
|
||||
else
|
||||
FreeLibrary( hMod );
|
||||
}
|
||||
|
||||
CloseHandle( hProcess );
|
||||
Mem_Free( data );
|
||||
return;
|
||||
}
|
||||
|
||||
qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname, qboolean directpath )
|
||||
{
|
||||
PIMAGE_NT_HEADERS peHeader;
|
||||
PIMAGE_IMPORT_DESCRIPTOR importDesc;
|
||||
byte *data;
|
||||
dll_user_t *hInst;
|
||||
qboolean ret = FALSE;
|
||||
|
||||
hInst = FS_FindLibrary( name, directpath );
|
||||
if ( !hInst ) return FALSE;
|
||||
|
||||
data = FS_LoadFile( name, NULL, false );
|
||||
if ( !data )
|
||||
{
|
||||
COM_FreeLibrary( hInst );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
importDesc = GetImportDescriptor( name, data, &peHeader );
|
||||
if ( !importDesc )
|
||||
{
|
||||
COM_FreeLibrary( hInst );
|
||||
Mem_Free( data );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR ) ) && importDesc->Name; importDesc++ )
|
||||
{
|
||||
const char *importName = (const char *)CALCULATE_ADDRESS( data, GetOffsetByRVA( importDesc->Name, peHeader ) );
|
||||
Con_Reportf( "library %s has direct dependency %s\n", name, importName );
|
||||
|
||||
if( !Q_stricmp( importName, depname ) )
|
||||
if ( !Q_stricmp( importName, depname ) )
|
||||
{
|
||||
COM_FreeLibrary( hInst );
|
||||
Mem_Free( data );
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
libraryerror:
|
||||
if( errorstring[0] )
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: %s\n", __FUNCTION__, errorstring );
|
||||
}
|
||||
if( data ) Mem_Free( data ); // release memory
|
||||
return false;
|
||||
COM_FreeLibrary( hInst );
|
||||
Mem_Free( data );
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -841,12 +447,19 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d
|
||||
{
|
||||
dll_user_t *hInst;
|
||||
|
||||
COM_ResetLibraryError();
|
||||
|
||||
hInst = FS_FindLibrary( dllname, directpath );
|
||||
if( !hInst ) return NULL; // nothing to load
|
||||
if( !hInst )
|
||||
{
|
||||
COM_PushLibraryError( va( "Failed to find library %s", dllname ) );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if( hInst->encrypted )
|
||||
{
|
||||
Con_Printf( S_ERROR "LoadLibrary: couldn't load encrypted library %s\n", dllname );
|
||||
COM_PushLibraryError( va( "Library %s is encrypted, cannot load", hInst->shortPath ) );
|
||||
COM_FreeLibrary( hInst );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -863,7 +476,11 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d
|
||||
|
||||
if( !hInst->hInstance )
|
||||
{
|
||||
Con_Reportf( "LoadLibrary: Loading %s - failed\n", dllname );
|
||||
COM_PushLibraryError( GetLastErrorAsString() );
|
||||
|
||||
if ( GetLastError() == ERROR_MOD_NOT_FOUND )
|
||||
ListMissingModules( hInst );
|
||||
|
||||
COM_FreeLibrary( hInst );
|
||||
return NULL;
|
||||
}
|
||||
@ -873,14 +490,12 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d
|
||||
{
|
||||
if( !LibraryLoadSymbols( hInst ))
|
||||
{
|
||||
Con_Reportf( "LoadLibrary: Loading %s - failed\n", dllname );
|
||||
COM_PushLibraryError( va( "Failed to load library %s", dllname ) );
|
||||
COM_FreeLibrary( hInst );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Con_Reportf( "LoadLibrary: Loading %s - ok\n", dllname );
|
||||
|
||||
return hInst;
|
||||
}
|
||||
|
||||
@ -944,7 +559,7 @@ void *COM_FunctionFromName( void *hInstance, const char *pName )
|
||||
if( !Q_strcmp( pName, hInst->names[i] ))
|
||||
{
|
||||
index = hInst->ordinals[i];
|
||||
return hInst->funcs[index] + hInst->funcBase;
|
||||
return (void *)( hInst->funcs[index] + hInst->funcBase );
|
||||
}
|
||||
}
|
||||
|
||||
|
24
engine/platform/win32/lib_win.h
Normal file
24
engine/platform/win32/lib_win.h
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
lib_win.h - common win32 dll definitions
|
||||
Copyright (C) 2022 Flying With Gauss
|
||||
|
||||
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 "library.h"
|
||||
#include <winnt.h>
|
||||
#include <psapi.h>
|
||||
|
||||
#define CALCULATE_ADDRESS( base, offset ) ( ( DWORD )( base ) + ( DWORD )( offset ) )
|
||||
|
||||
FARPROC MemoryGetProcAddress( void *module, const char *name );
|
||||
void MemoryFreeLibrary( void *hInstance );
|
||||
void *MemoryLoadLibrary( const char *name );
|
Loading…
x
Reference in New Issue
Block a user