1 /****************************************************************************
3 * SciTech MGL Graphics Library
5 * ========================================================================
7 * The contents of this file are subject to the SciTech MGL Public
8 * License Version 1.0 (the "License"); you may not use this file
9 * except in compliance with the License. You may obtain a copy of
10 * the License at http://www.scitechsoft.com/mgl-license.txt
12 * Software distributed under the License is distributed on an
13 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
17 * The Original Code is Copyright (C) 1991-1998 SciTech Software, Inc.
19 * The Initial Developer of the Original Code is SciTech Software, Inc.
20 * All Rights Reserved.
22 * ========================================================================
27 * Description: Module to implement a simple Portable Binary DLL loader
28 * library. This library can be used to load PE DLL's under
29 * any Intel based OS, provided the DLL's do not have any
30 * imports in the import table.
32 * NOTE: This loader module expects the DLL's to be built with
33 * Watcom C++ and may produce unexpected results with
34 * DLL's linked by another compiler.
36 ****************************************************************************/
38 #include "drvlib/peloader.h"
40 #include "drvlib/os/os.h"
41 #include "drvlib/libc/init.h"
42 #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED)
43 #define WIN32_LEAN_AND_MEAN
47 #include "drvlib/pe.h"
49 /*--------------------------- Global variables ----------------------------*/
51 static int result = PE_ok;
53 /*------------------------- Implementation --------------------------------*/
55 /****************************************************************************
57 f - Handle to open file to read driver from
58 startOffset - Offset to the start of the driver within the file
61 Handle to loaded PE DLL, or NULL on failure.
64 This function loads a Portable Binary DLL library from disk, relocates
65 the code and returns a handle to the loaded library. This function is the
66 same as the regular PE_loadLibrary except that it take a handle to an
67 open file and an offset within that file for the DLL to load.
68 ****************************************************************************/
69 static int PE_readHeader(
76 ulong offset,signature;
78 /* Read the EXE header and check for valid header signature */
79 result = PE_invalidDLLImage;
80 fseek(f, startOffset, SEEK_SET);
81 if (fread(&exehdr, 1, sizeof(exehdr), f) != sizeof(exehdr))
83 if (exehdr.signature != 0x5A4D)
86 /* Now seek to the start of the PE header defined at offset 0x3C
87 * in the MS-DOS EXE header, and read the signature and check it.
89 fseek(f, startOffset+0x3C, SEEK_SET);
90 if (fread(&offset, 1, sizeof(offset), f) != sizeof(offset))
92 fseek(f, startOffset+offset, SEEK_SET);
93 if (fread(&signature, 1, sizeof(signature), f) != sizeof(signature))
95 if (signature != 0x00004550)
98 /* Now read the PE file header and check that it is correct */
99 if (fread(filehdr, 1, sizeof(*filehdr), f) != sizeof(*filehdr))
101 if (filehdr->Machine != IMAGE_FILE_MACHINE_I386)
103 if (!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE))
105 if (!(filehdr->Characteristics & IMAGE_FILE_DLL))
107 if (fread(opthdr, 1, sizeof(*opthdr), f) != sizeof(*opthdr))
109 if (opthdr->Magic != 0x10B)
112 /* Success, so return true! */
116 /****************************************************************************
118 f - Handle to open file to read driver from
119 startOffset - Offset to the start of the driver within the file
122 Size of the DLL file on disk, or -1 on error
125 This function scans the headers for a Portable Binary DLL to determine the
126 length of the DLL file on disk.
128 ****************************************************************************/
129 ulong PEAPI PE_getFileSize(
139 /* Read the PE file headers from disk */
140 if (!PE_readHeader(f,startOffset,&filehdr,&opthdr))
143 /* Scan all the section headers summing up the total size */
144 size = opthdr.SizeOfHeaders;
145 for (i = 0; i < filehdr.NumberOfSections; i++) {
146 if (fread(§hdr, 1, sizeof(secthdr), f) != sizeof(secthdr))
148 size += secthdr.SizeOfRawData;
153 /****************************************************************************
155 Loads a Portable Binary DLL into memory from an open file
161 f - Handle to open file to read driver from
162 startOffset - Offset to the start of the driver within the file
163 size - Place to store the size of the driver loaded
164 shared - True to load module into shared memory
167 Handle to loaded PE DLL, or NULL on failure.
170 This function loads a Portable Binary DLL library from disk, relocates
171 the code and returns a handle to the loaded library. This function is the
172 same as the regular PE_loadLibrary except that it take a handle to an
173 open file and an offset within that file for the DLL to load.
176 PE_loadLibrary, PE_getProcAddress, PE_freeLibrary
177 ****************************************************************************/
178 PE_MODULE * PEAPI PE_loadLibraryExt(
187 ulong offset,pageOffset;
188 ulong text_off,text_addr,text_size;
189 ulong data_off,data_addr,data_size,data_end;
190 ulong export_off,export_addr,export_size,export_end;
191 ulong reloc_off,reloc_size;
193 int i,delta,numFixups;
194 ushort relocType,*fixup;
195 PE_MODULE *hMod = NULL;
197 BASE_RELOCATION *baseReloc;
200 /* Read the PE file headers from disk */
201 if (!PE_readHeader(f,startOffset,&filehdr,&opthdr))
204 /* Scan all the section headers and find the necessary sections */
205 text_off = data_off = reloc_off = export_off = 0;
206 text_addr = text_size = 0;
207 data_addr = data_size = data_end = 0;
208 export_addr = export_size = export_end = 0;
210 for (i = 0; i < filehdr.NumberOfSections; i++) {
211 if (fread(§hdr, 1, sizeof(secthdr), f) != sizeof(secthdr))
213 if (strcmp(secthdr.Name, ".edata") == 0 || strcmp(secthdr.Name, ".rdata") == 0) {
214 /* Exports section */
215 export_off = secthdr.PointerToRawData;
216 export_addr = secthdr.VirtualAddress;
217 export_size = secthdr.SizeOfRawData;
218 export_end = export_addr + export_size;
220 else if (strcmp(secthdr.Name, ".idata") == 0) {
221 /* Imports section, ignore */
223 else if (strcmp(secthdr.Name, ".reloc") == 0) {
224 /* Relocations section */
225 reloc_off = secthdr.PointerToRawData;
226 reloc_size = secthdr.SizeOfRawData;
228 else if (!text_off && secthdr.Characteristics & IMAGE_SCN_CNT_CODE) {
230 text_off = secthdr.PointerToRawData;
231 text_addr = secthdr.VirtualAddress;
232 text_size = secthdr.SizeOfRawData;
234 else if (!data_off && secthdr.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) {
236 data_off = secthdr.PointerToRawData;
237 data_addr = secthdr.VirtualAddress;
238 data_size = secthdr.SizeOfRawData;
239 data_end = data_addr + data_size;
243 /* Check to make sure that we have all the sections we need */
244 if (!text_off || !data_off || !export_off || !reloc_off) {
245 result = PE_invalidDLLImage;
249 /* Find the size of the image to load allocate memory for it */
250 image_size = MAX(export_end,data_end) - text_addr;
251 *size = sizeof(PE_MODULE) + image_size + 4096;
253 hMod = PM_mallocShared(*size);
255 hMod = PM_malloc(*size);
256 reloc = PM_malloc(reloc_size);
257 if (!hMod || !reloc) {
258 result = PE_outOfMemory;
262 hMod->text = (uchar*)ROUND_4K((ulong)hMod + sizeof(PE_MODULE));
263 hMod->data = (uchar*)((ulong)hMod->text + (data_addr - text_addr));
264 hMod->export = (uchar*)((ulong)hMod->text + (export_addr - text_addr));
265 hMod->textBase = text_addr;
266 hMod->dataBase = data_addr;
267 hMod->exportBase = export_addr;
268 hMod->exportDir = opthdr.DataDirectory[0].RelVirtualAddress - export_addr;
269 hMod->shared = shared;
271 /* Now read the section images from disk */
272 result = PE_invalidDLLImage;
273 fseek(f, startOffset+text_off, SEEK_SET);
274 if (fread(hMod->text, 1, text_size, f) != text_size)
276 fseek(f, startOffset+data_off, SEEK_SET);
277 if (fread(hMod->data, 1, data_size, f) != data_size)
279 fseek(f, startOffset+export_off, SEEK_SET);
280 if (fread(hMod->export, 1, export_size, f) != export_size)
282 fseek(f, startOffset+reloc_off, SEEK_SET);
283 if (fread(reloc, 1, reloc_size, f) != reloc_size)
286 /* Now perform relocations on all sections in the image */
287 delta = (ulong)hMod->text - opthdr.ImageBase - text_addr;
288 baseReloc = (BASE_RELOCATION*)reloc;
290 /* Check for termination condition */
291 if (!baseReloc->PageRVA || !baseReloc->BlockSize)
295 pageOffset = baseReloc->PageRVA - hMod->textBase;
296 numFixups = (baseReloc->BlockSize - sizeof(BASE_RELOCATION)) / sizeof(ushort);
297 fixup = (ushort*)(baseReloc + 1);
298 for (i = 0; i < numFixups; i++) {
299 relocType = *fixup >> 12;
301 offset = pageOffset + (*fixup & 0x0FFF);
302 *(ulong*)(hMod->text + offset) += delta;
307 /* Move to next relocation block */
308 baseReloc = (BASE_RELOCATION*)((ulong)baseReloc + baseReloc->BlockSize);
311 /* Initialise the C runtime library for the loaded DLL */
312 result = PE_unableToInitLibC;
313 if ((InitLibC = (InitLibC_t)PE_getProcAddress(hMod,"_InitLibC")) == NULL)
315 if (!InitLibC(&___imports,PM_getOSType()))
318 /* Clean up, close the file and return the loaded module handle */
332 /****************************************************************************
334 Loads a Portable Binary DLL into memory
340 szDLLName - Name of the PE DLL library to load
341 shared - True to load module into shared memory
344 Handle to loaded PE DLL, or NULL on failure.
347 This function loads a Portable Binary DLL library from disk, relocates
348 the code and returns a handle to the loaded library. This function
349 will only work on DLL's that do not have any imports, since we don't
350 resolve import dependencies in this function.
353 PE_getProcAddress, PE_freeLibrary
354 ****************************************************************************/
355 PE_MODULE * PEAPI PE_loadLibrary(
356 const char *szDLLName,
361 #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED)
366 /* For Win32 if are building checked libraries for debugging, we use
367 * the real Win32 DLL functions so that we can debug the resulting DLL
368 * files with the Win32 debuggers. Note that we can't do this if
369 * we need to load the files into a shared memory context.
371 if ((hInst = PM_loadLibrary(szDLLName)) == NULL) {
372 result = PE_fileNotFound;
376 /* Initialise the C runtime library for the loaded DLL */
377 result = PE_unableToInitLibC;
378 if ((InitLibC = (void*)PM_getProcAddress(hInst,"_InitLibC")) == NULL)
380 if (!InitLibC(&___imports,PM_getOSType()))
383 /* Allocate the PE_MODULE structure */
384 if ((hMod = PM_malloc(sizeof(*hMod))) == NULL)
386 hMod->text = (void*)hInst;
389 /* DLL loaded successfully so return module handle */
399 /* Attempt to open the file on disk */
402 if ((f = fopen(szDLLName,"rb")) == NULL) {
403 result = PE_fileNotFound;
406 hMod = PE_loadLibraryExt(f,0,&size,shared);
412 /****************************************************************************
414 Loads a Portable Binary DLL into memory
420 szDLLName - Name of the PE DLL library to load
421 shared - True to load module into shared memory
424 Handle to loaded PE DLL, or NULL on failure.
427 This function is the same as the regular PE_loadLibrary function, except
428 that it looks for the drivers in the MGL_ROOT/drivers directory or a
429 /drivers directory relative to the current directory.
432 PE_loadLibraryMGL, PE_getProcAddress, PE_freeLibrary
433 ****************************************************************************/
434 PE_MODULE * PEAPI PE_loadLibraryMGL(
435 const char *szDLLName,
438 #if !defined(__WIN32_VXD__) && !defined(__NT_DRIVER__)
443 /* We look in the 'drivers' directory, optionally under the MGL_ROOT
444 * environment variable directory.
446 #if !defined(__WIN32_VXD__) && !defined(__NT_DRIVER__)
447 if (getenv("MGL_ROOT")) {
448 strcpy(path,getenv("MGL_ROOT"));
451 strcat(path,"drivers");
453 strcat(path,szDLLName);
454 if ((hMod = PE_loadLibrary(path,shared)) != NULL)
457 strcpy(path,"drivers");
459 strcat(path,szDLLName);
460 return PE_loadLibrary(path,shared);
463 /****************************************************************************
465 Gets a function address from a Portable Binary DLL
471 hModule - Handle to a loaded PE DLL library
472 szProcName - Name of the function to get the address of
475 Pointer to the function, or NULL on failure.
478 This function searches for the named, exported function in a loaded PE
479 DLL library, and returns the address of the function. If the function is
480 not found in the library, this function return NULL.
483 PE_loadLibrary, PE_freeLibrary
484 ****************************************************************************/
485 void * PEAPI PE_getProcAddress(
487 const char *szProcName)
489 #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED)
490 if (hModule->shared == -1)
491 return (void*)PM_getProcAddress(hModule->text,szProcName);
496 EXPORT_DIRECTORY *exports;
500 ushort *OrdinalTable;
503 /* Find the address of the export tables from the export section */
506 exports = (EXPORT_DIRECTORY*)(hModule->export + hModule->exportDir);
507 AddressTable = (ulong*)(hModule->export + exports->AddressTableRVA - hModule->exportBase);
508 NameTable = (ulong*)(hModule->export + exports->NameTableRVA - hModule->exportBase);
509 OrdinalTable = (ushort*)(hModule->export + exports->OrdinalTableRVA - hModule->exportBase);
511 /* Search the export name table to find the function name */
512 for (i = 0; i < exports->NumberOfNamePointers; i++) {
513 name = (char*)(hModule->export + NameTable[i] - hModule->exportBase);
514 if (strcmp(name,szProcName) == 0)
517 if (i == exports->NumberOfNamePointers)
519 funcOffset = AddressTable[OrdinalTable[i]];
522 return (void*)(hModule->text + funcOffset - hModule->textBase);
526 /****************************************************************************
528 Frees a loaded Portable Binary DLL
534 hModule - Handle to a loaded PE DLL library to free
537 This function frees a loaded PE DLL library from memory.
540 PE_getProcAddress, PE_loadLibrary
541 ****************************************************************************/
542 void PEAPI PE_freeLibrary(
545 TerminateLibC_t TerminateLibC;
547 #if (defined(__WINDOWS32__) || defined(__DRIVER__)) && defined(CHECKED)
548 if (hModule->shared == -1) {
549 /* Run the C runtime library exit code on module unload */
550 if ((TerminateLibC = (TerminateLibC_t)PM_getProcAddress(hModule->text,"_TerminateLibC")) != NULL)
552 PM_freeLibrary(hModule->text);
559 /* Run the C runtime library exit code on module unload */
560 if ((TerminateLibC = (TerminateLibC_t)PE_getProcAddress(hModule,"_TerminateLibC")) != NULL)
563 PM_freeShared(hModule);
570 /****************************************************************************
572 Returns the error code for the last operation
578 Error code for the last operation.
581 PE_getProcAddress, PE_loadLibrary
582 ****************************************************************************/
583 int PEAPI PE_getError(void)