| /**************************************************************************** |
| * |
| * SciTech MGL Graphics Library |
| * |
| * ======================================================================== |
| * |
| * The contents of this file are subject to the SciTech MGL Public |
| * License Version 1.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.scitechsoft.com/mgl-license.txt |
| * |
| * Software distributed under the License is distributed on an |
| * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * rights and limitations under the License. |
| * |
| * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc. |
| * |
| * The Initial Developer of the Original Code is SciTech Software, Inc. |
| * All Rights Reserved. |
| * |
| * ======================================================================== |
| * |
| * Language: ANSI C |
| * Environment: Any |
| * |
| * Description: Module to implement a simple Portable Binary DLL loader |
| * library. This library can be used to load PE DLL's under |
| * any Intel based OS, provided the DLL's do not have any |
| * imports in the import table. |
| * |
| * NOTE: This loader module expects the DLL's to be built with |
| * Watcom C++ and may produce unexpected results with |
| * DLL's linked by another compiler. |
| * |
| ****************************************************************************/ |
| |
| #include "drvlib/peloader.h" |
| #include "pmapi.h" |
| #include "drvlib/os/os.h" |
| #include "drvlib/libc/init.h" |
| #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED) |
| #define WIN32_LEAN_AND_MEAN |
| #define STRICT |
| #include <windows.h> |
| #endif |
| #include "drvlib/pe.h" |
| |
| /*--------------------------- Global variables ----------------------------*/ |
| |
| static int result = PE_ok; |
| |
| /*------------------------- Implementation --------------------------------*/ |
| |
| /**************************************************************************** |
| PARAMETERS: |
| f - Handle to open file to read driver from |
| startOffset - Offset to the start of the driver within the file |
| |
| RETURNS: |
| Handle to loaded PE DLL, or NULL on failure. |
| |
| REMARKS: |
| This function loads a Portable Binary DLL library from disk, relocates |
| the code and returns a handle to the loaded library. This function is the |
| same as the regular PE_loadLibrary except that it take a handle to an |
| open file and an offset within that file for the DLL to load. |
| ****************************************************************************/ |
| static int PE_readHeader( |
| FILE *f, |
| long startOffset, |
| FILE_HDR *filehdr, |
| OPTIONAL_HDR *opthdr) |
| { |
| EXE_HDR exehdr; |
| ulong offset,signature; |
| |
| /* Read the EXE header and check for valid header signature */ |
| result = PE_invalidDLLImage; |
| fseek(f, startOffset, SEEK_SET); |
| if (fread(&exehdr, 1, sizeof(exehdr), f) != sizeof(exehdr)) |
| return false; |
| if (exehdr.signature != 0x5A4D) |
| return false; |
| |
| /* Now seek to the start of the PE header defined at offset 0x3C |
| * in the MS-DOS EXE header, and read the signature and check it. |
| */ |
| fseek(f, startOffset+0x3C, SEEK_SET); |
| if (fread(&offset, 1, sizeof(offset), f) != sizeof(offset)) |
| return false; |
| fseek(f, startOffset+offset, SEEK_SET); |
| if (fread(&signature, 1, sizeof(signature), f) != sizeof(signature)) |
| return false; |
| if (signature != 0x00004550) |
| return false; |
| |
| /* Now read the PE file header and check that it is correct */ |
| if (fread(filehdr, 1, sizeof(*filehdr), f) != sizeof(*filehdr)) |
| return false; |
| if (filehdr->Machine != IMAGE_FILE_MACHINE_I386) |
| return false; |
| if (!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) |
| return false; |
| if (!(filehdr->Characteristics & IMAGE_FILE_DLL)) |
| return false; |
| if (fread(opthdr, 1, sizeof(*opthdr), f) != sizeof(*opthdr)) |
| return false; |
| if (opthdr->Magic != 0x10B) |
| return false; |
| |
| /* Success, so return true! */ |
| return true; |
| } |
| |
| /**************************************************************************** |
| PARAMETERS: |
| f - Handle to open file to read driver from |
| startOffset - Offset to the start of the driver within the file |
| |
| RETURNS: |
| Size of the DLL file on disk, or -1 on error |
| |
| REMARKS: |
| This function scans the headers for a Portable Binary DLL to determine the |
| length of the DLL file on disk. |
| {secret} |
| ****************************************************************************/ |
| ulong PEAPI PE_getFileSize( |
| FILE *f, |
| ulong startOffset) |
| { |
| FILE_HDR filehdr; |
| OPTIONAL_HDR opthdr; |
| SECTION_HDR secthdr; |
| ulong size; |
| int i; |
| |
| /* Read the PE file headers from disk */ |
| if (!PE_readHeader(f,startOffset,&filehdr,&opthdr)) |
| return 0xFFFFFFFF; |
| |
| /* Scan all the section headers summing up the total size */ |
| size = opthdr.SizeOfHeaders; |
| for (i = 0; i < filehdr.NumberOfSections; i++) { |
| if (fread(§hdr, 1, sizeof(secthdr), f) != sizeof(secthdr)) |
| return 0xFFFFFFFF; |
| size += secthdr.SizeOfRawData; |
| } |
| return size; |
| } |
| |
| /**************************************************************************** |
| DESCRIPTION: |
| Loads a Portable Binary DLL into memory from an open file |
| |
| HEADER: |
| peloader.h |
| |
| PARAMETERS: |
| f - Handle to open file to read driver from |
| startOffset - Offset to the start of the driver within the file |
| size - Place to store the size of the driver loaded |
| shared - True to load module into shared memory |
| |
| RETURNS: |
| Handle to loaded PE DLL, or NULL on failure. |
| |
| REMARKS: |
| This function loads a Portable Binary DLL library from disk, relocates |
| the code and returns a handle to the loaded library. This function is the |
| same as the regular PE_loadLibrary except that it take a handle to an |
| open file and an offset within that file for the DLL to load. |
| |
| SEE ALSO: |
| PE_loadLibrary, PE_getProcAddress, PE_freeLibrary |
| ****************************************************************************/ |
| PE_MODULE * PEAPI PE_loadLibraryExt( |
| FILE *f, |
| ulong startOffset, |
| ulong *size, |
| ibool shared) |
| { |
| FILE_HDR filehdr; |
| OPTIONAL_HDR opthdr; |
| SECTION_HDR secthdr; |
| ulong offset,pageOffset; |
| ulong text_off,text_addr,text_size; |
| ulong data_off,data_addr,data_size,data_end; |
| ulong export_off,export_addr,export_size,export_end; |
| ulong reloc_off,reloc_size; |
| ulong image_size; |
| int i,delta,numFixups; |
| ushort relocType,*fixup; |
| PE_MODULE *hMod = NULL; |
| void *reloc = NULL; |
| BASE_RELOCATION *baseReloc; |
| InitLibC_t InitLibC; |
| |
| /* Read the PE file headers from disk */ |
| if (!PE_readHeader(f,startOffset,&filehdr,&opthdr)) |
| return NULL; |
| |
| /* Scan all the section headers and find the necessary sections */ |
| text_off = data_off = reloc_off = export_off = 0; |
| text_addr = text_size = 0; |
| data_addr = data_size = data_end = 0; |
| export_addr = export_size = export_end = 0; |
| reloc_size = 0; |
| for (i = 0; i < filehdr.NumberOfSections; i++) { |
| if (fread(§hdr, 1, sizeof(secthdr), f) != sizeof(secthdr)) |
| goto Error; |
| if (strcmp(secthdr.Name, ".edata") == 0 || strcmp(secthdr.Name, ".rdata") == 0) { |
| /* Exports section */ |
| export_off = secthdr.PointerToRawData; |
| export_addr = secthdr.VirtualAddress; |
| export_size = secthdr.SizeOfRawData; |
| export_end = export_addr + export_size; |
| } |
| else if (strcmp(secthdr.Name, ".idata") == 0) { |
| /* Imports section, ignore */ |
| } |
| else if (strcmp(secthdr.Name, ".reloc") == 0) { |
| /* Relocations section */ |
| reloc_off = secthdr.PointerToRawData; |
| reloc_size = secthdr.SizeOfRawData; |
| } |
| else if (!text_off && secthdr.Characteristics & IMAGE_SCN_CNT_CODE) { |
| /* Code section */ |
| text_off = secthdr.PointerToRawData; |
| text_addr = secthdr.VirtualAddress; |
| text_size = secthdr.SizeOfRawData; |
| } |
| else if (!data_off && secthdr.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) { |
| /* Data section */ |
| data_off = secthdr.PointerToRawData; |
| data_addr = secthdr.VirtualAddress; |
| data_size = secthdr.SizeOfRawData; |
| data_end = data_addr + data_size; |
| } |
| } |
| |
| /* Check to make sure that we have all the sections we need */ |
| if (!text_off || !data_off || !export_off || !reloc_off) { |
| result = PE_invalidDLLImage; |
| goto Error; |
| } |
| |
| /* Find the size of the image to load allocate memory for it */ |
| image_size = MAX(export_end,data_end) - text_addr; |
| *size = sizeof(PE_MODULE) + image_size + 4096; |
| if (shared) |
| hMod = PM_mallocShared(*size); |
| else |
| hMod = PM_malloc(*size); |
| reloc = PM_malloc(reloc_size); |
| if (!hMod || !reloc) { |
| result = PE_outOfMemory; |
| goto Error; |
| } |
| |
| hMod->text = (uchar*)ROUND_4K((ulong)hMod + sizeof(PE_MODULE)); |
| hMod->data = (uchar*)((ulong)hMod->text + (data_addr - text_addr)); |
| hMod->export = (uchar*)((ulong)hMod->text + (export_addr - text_addr)); |
| hMod->textBase = text_addr; |
| hMod->dataBase = data_addr; |
| hMod->exportBase = export_addr; |
| hMod->exportDir = opthdr.DataDirectory[0].RelVirtualAddress - export_addr; |
| hMod->shared = shared; |
| |
| /* Now read the section images from disk */ |
| result = PE_invalidDLLImage; |
| fseek(f, startOffset+text_off, SEEK_SET); |
| if (fread(hMod->text, 1, text_size, f) != text_size) |
| goto Error; |
| fseek(f, startOffset+data_off, SEEK_SET); |
| if (fread(hMod->data, 1, data_size, f) != data_size) |
| goto Error; |
| fseek(f, startOffset+export_off, SEEK_SET); |
| if (fread(hMod->export, 1, export_size, f) != export_size) |
| goto Error; |
| fseek(f, startOffset+reloc_off, SEEK_SET); |
| if (fread(reloc, 1, reloc_size, f) != reloc_size) |
| goto Error; |
| |
| /* Now perform relocations on all sections in the image */ |
| delta = (ulong)hMod->text - opthdr.ImageBase - text_addr; |
| baseReloc = (BASE_RELOCATION*)reloc; |
| for (;;) { |
| /* Check for termination condition */ |
| if (!baseReloc->PageRVA || !baseReloc->BlockSize) |
| break; |
| |
| /* Do fixups */ |
| pageOffset = baseReloc->PageRVA - hMod->textBase; |
| numFixups = (baseReloc->BlockSize - sizeof(BASE_RELOCATION)) / sizeof(ushort); |
| fixup = (ushort*)(baseReloc + 1); |
| for (i = 0; i < numFixups; i++) { |
| relocType = *fixup >> 12; |
| if (relocType) { |
| offset = pageOffset + (*fixup & 0x0FFF); |
| *(ulong*)(hMod->text + offset) += delta; |
| } |
| fixup++; |
| } |
| |
| /* Move to next relocation block */ |
| baseReloc = (BASE_RELOCATION*)((ulong)baseReloc + baseReloc->BlockSize); |
| } |
| |
| /* Initialise the C runtime library for the loaded DLL */ |
| result = PE_unableToInitLibC; |
| if ((InitLibC = (InitLibC_t)PE_getProcAddress(hMod,"_InitLibC")) == NULL) |
| goto Error; |
| if (!InitLibC(&___imports,PM_getOSType())) |
| goto Error; |
| |
| /* Clean up, close the file and return the loaded module handle */ |
| PM_free(reloc); |
| result = PE_ok; |
| return hMod; |
| |
| Error: |
| if (shared) |
| PM_freeShared(hMod); |
| else |
| PM_free(hMod); |
| PM_free(reloc); |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| DESCRIPTION: |
| Loads a Portable Binary DLL into memory |
| |
| HEADER: |
| peloader.h |
| |
| PARAMETERS: |
| szDLLName - Name of the PE DLL library to load |
| shared - True to load module into shared memory |
| |
| RETURNS: |
| Handle to loaded PE DLL, or NULL on failure. |
| |
| REMARKS: |
| This function loads a Portable Binary DLL library from disk, relocates |
| the code and returns a handle to the loaded library. This function |
| will only work on DLL's that do not have any imports, since we don't |
| resolve import dependencies in this function. |
| |
| SEE ALSO: |
| PE_getProcAddress, PE_freeLibrary |
| ****************************************************************************/ |
| PE_MODULE * PEAPI PE_loadLibrary( |
| const char *szDLLName, |
| ibool shared) |
| { |
| PE_MODULE *hMod; |
| |
| #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED) |
| if (!shared) { |
| PM_MODULE hInst; |
| InitLibC_t InitLibC; |
| |
| /* For Win32 if are building checked libraries for debugging, we use |
| * the real Win32 DLL functions so that we can debug the resulting DLL |
| * files with the Win32 debuggers. Note that we can't do this if |
| * we need to load the files into a shared memory context. |
| */ |
| if ((hInst = PM_loadLibrary(szDLLName)) == NULL) { |
| result = PE_fileNotFound; |
| return NULL; |
| } |
| |
| /* Initialise the C runtime library for the loaded DLL */ |
| result = PE_unableToInitLibC; |
| if ((InitLibC = (void*)PM_getProcAddress(hInst,"_InitLibC")) == NULL) |
| return NULL; |
| if (!InitLibC(&___imports,PM_getOSType())) |
| return NULL; |
| |
| /* Allocate the PE_MODULE structure */ |
| if ((hMod = PM_malloc(sizeof(*hMod))) == NULL) |
| return NULL; |
| hMod->text = (void*)hInst; |
| hMod->shared = -1; |
| |
| /* DLL loaded successfully so return module handle */ |
| result = PE_ok; |
| return hMod; |
| } |
| else |
| #endif |
| { |
| FILE *f; |
| ulong size; |
| |
| /* Attempt to open the file on disk */ |
| if (shared < 0) |
| shared = 0; |
| if ((f = fopen(szDLLName,"rb")) == NULL) { |
| result = PE_fileNotFound; |
| return NULL; |
| } |
| hMod = PE_loadLibraryExt(f,0,&size,shared); |
| fclose(f); |
| return hMod; |
| } |
| } |
| |
| /**************************************************************************** |
| DESCRIPTION: |
| Loads a Portable Binary DLL into memory |
| |
| HEADER: |
| peloader.h |
| |
| PARAMETERS: |
| szDLLName - Name of the PE DLL library to load |
| shared - True to load module into shared memory |
| |
| RETURNS: |
| Handle to loaded PE DLL, or NULL on failure. |
| |
| REMARKS: |
| This function is the same as the regular PE_loadLibrary function, except |
| that it looks for the drivers in the MGL_ROOT/drivers directory or a |
| /drivers directory relative to the current directory. |
| |
| SEE ALSO: |
| PE_loadLibraryMGL, PE_getProcAddress, PE_freeLibrary |
| ****************************************************************************/ |
| PE_MODULE * PEAPI PE_loadLibraryMGL( |
| const char *szDLLName, |
| ibool shared) |
| { |
| #if !defined(__WIN32_VXD__) && !defined(__NT_DRIVER__) |
| PE_MODULE *hMod; |
| #endif |
| char path[256] = ""; |
| |
| /* We look in the 'drivers' directory, optionally under the MGL_ROOT |
| * environment variable directory. |
| */ |
| #if !defined(__WIN32_VXD__) && !defined(__NT_DRIVER__) |
| if (getenv("MGL_ROOT")) { |
| strcpy(path,getenv("MGL_ROOT")); |
| PM_backslash(path); |
| } |
| strcat(path,"drivers"); |
| PM_backslash(path); |
| strcat(path,szDLLName); |
| if ((hMod = PE_loadLibrary(path,shared)) != NULL) |
| return hMod; |
| #endif |
| strcpy(path,"drivers"); |
| PM_backslash(path); |
| strcat(path,szDLLName); |
| return PE_loadLibrary(path,shared); |
| } |
| |
| /**************************************************************************** |
| DESCRIPTION: |
| Gets a function address from a Portable Binary DLL |
| |
| HEADER: |
| peloader.h |
| |
| PARAMETERS: |
| hModule - Handle to a loaded PE DLL library |
| szProcName - Name of the function to get the address of |
| |
| RETURNS: |
| Pointer to the function, or NULL on failure. |
| |
| REMARKS: |
| This function searches for the named, exported function in a loaded PE |
| DLL library, and returns the address of the function. If the function is |
| not found in the library, this function return NULL. |
| |
| SEE ALSO: |
| PE_loadLibrary, PE_freeLibrary |
| ****************************************************************************/ |
| void * PEAPI PE_getProcAddress( |
| PE_MODULE *hModule, |
| const char *szProcName) |
| { |
| #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED) |
| if (hModule->shared == -1) |
| return (void*)PM_getProcAddress(hModule->text,szProcName); |
| else |
| #endif |
| { |
| uint i; |
| EXPORT_DIRECTORY *exports; |
| ulong funcOffset; |
| ulong *AddressTable; |
| ulong *NameTable; |
| ushort *OrdinalTable; |
| char *name; |
| |
| /* Find the address of the export tables from the export section */ |
| if (!hModule) |
| return NULL; |
| exports = (EXPORT_DIRECTORY*)(hModule->export + hModule->exportDir); |
| AddressTable = (ulong*)(hModule->export + exports->AddressTableRVA - hModule->exportBase); |
| NameTable = (ulong*)(hModule->export + exports->NameTableRVA - hModule->exportBase); |
| OrdinalTable = (ushort*)(hModule->export + exports->OrdinalTableRVA - hModule->exportBase); |
| |
| /* Search the export name table to find the function name */ |
| for (i = 0; i < exports->NumberOfNamePointers; i++) { |
| name = (char*)(hModule->export + NameTable[i] - hModule->exportBase); |
| if (strcmp(name,szProcName) == 0) |
| break; |
| } |
| if (i == exports->NumberOfNamePointers) |
| return NULL; |
| funcOffset = AddressTable[OrdinalTable[i]]; |
| if (!funcOffset) |
| return NULL; |
| return (void*)(hModule->text + funcOffset - hModule->textBase); |
| } |
| } |
| |
| /**************************************************************************** |
| DESCRIPTION: |
| Frees a loaded Portable Binary DLL |
| |
| HEADER: |
| peloader.h |
| |
| PARAMETERS: |
| hModule - Handle to a loaded PE DLL library to free |
| |
| REMARKS: |
| This function frees a loaded PE DLL library from memory. |
| |
| SEE ALSO: |
| PE_getProcAddress, PE_loadLibrary |
| ****************************************************************************/ |
| void PEAPI PE_freeLibrary( |
| PE_MODULE *hModule) |
| { |
| TerminateLibC_t TerminateLibC; |
| |
| #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED) |
| if (hModule->shared == -1) { |
| /* Run the C runtime library exit code on module unload */ |
| if ((TerminateLibC = (TerminateLibC_t)PM_getProcAddress(hModule->text,"_TerminateLibC")) != NULL) |
| TerminateLibC(); |
| PM_freeLibrary(hModule->text); |
| PM_free(hModule); |
| } |
| else |
| #endif |
| { |
| if (hModule) { |
| /* Run the C runtime library exit code on module unload */ |
| if ((TerminateLibC = (TerminateLibC_t)PE_getProcAddress(hModule,"_TerminateLibC")) != NULL) |
| TerminateLibC(); |
| if (hModule->shared) |
| PM_freeShared(hModule); |
| else |
| PM_free(hModule); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| DESCRIPTION: |
| Returns the error code for the last operation |
| |
| HEADER: |
| peloader.h |
| |
| RETURNS: |
| Error code for the last operation. |
| |
| SEE ALSO: |
| PE_getProcAddress, PE_loadLibrary |
| ****************************************************************************/ |
| int PEAPI PE_getError(void) |
| { |
| return result; |
| } |