/**************************************************************************** * * 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 #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; }