source: trunk/src/kernel32/winimagepe2lx.cpp@ 2802

Last change on this file since 2802 was 2802, checked in by sandervl, 26 years ago

Added new logging feature

File size: 20.7 KB
Line 
1/* $Id: winimagepe2lx.cpp,v 1.8 2000-02-16 14:22:12 sandervl Exp $ */
2
3/*
4 * Win32 PE2LX Image base class
5 *
6 * Copyright 1998-1999 Sander van Leeuwen (sandervl@xs4all.nl)
7 * Copyright 1998-1999 knut st. osmundsen (knut.stange.osmundsen@pmsc.no)
8 *
9 * Project Odin Software License can be found in LICENSE.TXT
10 *
11 */
12
13/*******************************************************************************
14* Defined Constants And Macros *
15*******************************************************************************/
16#define INCL_DOSERRORS /* DOS Error values */
17#define INCL_DOSPROFILE /* DosQuerySysState (Toolkit 4.5) */
18#define INCL_DOSMODULEMGR /* DOS Module management */
19
20#define ALIGN(a, alignment) (((a) + (alignment - 1UL)) & ~(alignment - 1UL))
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#include <os2wrap.h> //Odin32 OS/2 api wrappers
27
28#include <malloc.h>
29#include <process.h>
30#include <stdlib.h>
31
32#include <win32type.h>
33#include <misc.h>
34#include <winimagebase.h>
35#include <winimagepe2lx.h>
36
37#define DBG_LOCALLOG DBG_winimagepe2lx
38#include "dbglocal.h"
39
40/*******************************************************************************
41* Structures and Typedefs *
42*******************************************************************************/
43#ifndef QS_MTE
44 /* From OS/2 Toolkit v4.5 (BSEDOS.H) */
45
46 /* Global Record structure
47 * Holds all global system information. Placed first in user buffer
48 */
49 typedef struct qsGrec_s { /* qsGrec */
50 ULONG cThrds;
51 ULONG c32SSem;
52 ULONG cMFTNodes;
53 }qsGrec_t;
54
55 /*
56 * System wide MTE information
57 * ________________________________
58 * | pNextRec |----|
59 * |-------------------------------| |
60 * | hmte | |
61 * |-------------------------------| |
62 * | ctImpMod | |
63 * |-------------------------------| |
64 * | ctObj | |
65 * |-------------------------------| |
66 * | pObjInfo |----|----------|
67 * |-------------------------------| | |
68 * | pName |----|----| |
69 * |-------------------------------| | | |
70 * | imported module handles | | | |
71 * | . | | | |
72 * | . | | | |
73 * | . | | | |
74 * |-------------------------------| <--|----| |
75 * | "pathname" | | |
76 * |-------------------------------| <--|----------|
77 * | Object records | |
78 * | (if requested) | |
79 * |_______________________________| |
80 * <-----
81 * NOTE that if the level bit is set to QS_MTE, the base Lib record will be followed
82 * by a series of object records (qsLObj_t); one for each object of the
83 * module.
84 */
85
86 typedef struct qsLObjrec_s { /* qsLOrec */
87 ULONG oaddr; /* object address */
88 ULONG osize; /* object size */
89 ULONG oflags; /* object flags */
90 } qsLObjrec_t;
91
92 typedef struct qsLrec_s { /* qsLrec */
93 void FAR *pNextRec; /* pointer to next record in buffer */
94 USHORT hmte; /* handle for this mte */
95 USHORT fFlat; /* true if 32 bit module */
96 ULONG ctImpMod; /* # of imported modules in table */
97 ULONG ctObj; /* # of objects in module (mte_objcnt)*/
98 qsLObjrec_t FAR *pObjInfo; /* pointer to per object info if any */
99 UCHAR FAR *pName; /* -> name string following struc */
100 } qsLrec_t;
101
102#endif
103
104
105/*******************************************************************************
106* External Functions *
107*******************************************************************************/
108#ifndef QS_MTE
109 /* from OS/2 Toolkit v4.5 */
110
111 APIRET APIENTRY DosQuerySysState(ULONG EntityList, ULONG EntityLevel, PID pid,
112 TID tid, PVOID pDataBuf, ULONG cbBuf);
113 #define QS_MTE 0x0004
114#endif
115
116
117
118/**
119 * Constructor - creates an pe2lx image object from a module handle to a pe2lx module.
120 * @param hinstance OS/2 module handle.
121 * @param fWin32k TRUE: Win32k module.
122 * FALSE: Pe2Lx module.
123 * @status partially implemented.
124 * @author knut st. osmundsen, Sander van Leeuwen
125 */
126Win32Pe2LxImage::Win32Pe2LxImage(HINSTANCE hinstance, BOOL fWin32k)
127 : Win32ImageBase(hinstance),
128 paSections(NULL), cSections(0), pNtHdrs(NULL), fWin32k(fWin32k)
129{
130 setFullPath(szFileName);
131}
132
133
134/**
135 * Free memory associated with this object.
136 * @status completely implemented.
137 * @author knut st. osmundsen, Sander van Leeuwen
138 */
139Win32Pe2LxImage::~Win32Pe2LxImage()
140{
141 cleanup();
142}
143
144
145/**
146 * Initiates the object.
147 * Must be called immediately after the object construction.
148 * @returns Success indicator, TRUE == success; FALSE = failure.
149 * @sketch Get section placement and sizes for this module. (paSections, cSections)
150 * Verify that there is at least one section - the header section.
151 * Locate the NT headers.
152 * Set pNtHdrs pointer.
153 * Validate the NT headers.
154 * Read the PE section table the set the RVAs in paSections.
155 * Locate and set the entrypoint.
156 * Locate the resource directory (if any). (pResDir, ulRVAResourceSection)
157 * TLS - FIXME!
158 * @status completely implemented.
159 * @author knut st. osmundsen
160 * @remark Object must be destroyed if failure!
161 */
162BOOL Win32Pe2LxImage::init()
163{
164 APIRET rc;
165
166 /* Get section placement and sizes for this module. (paSections, cSections) */
167 rc = getSections();
168 if (rc != NO_ERROR)
169 {
170 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: error - getSection failed with rc=%d\n",
171 rc));
172 return FALSE;
173 }
174
175 /* Verify that there is at least one section - the header section. */
176 if (cSections < 1)
177 {
178 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: no header section, cSections is 0\n"));
179 return FALSE;
180 }
181
182 /* Locate the NT headers. */
183 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)paSections[0].ulAddress;
184 if ((pDosHdr->e_magic != IMAGE_DOS_SIGNATURE
185 || pDosHdr->e_lfanew < sizeof(IMAGE_DOS_HEADER) /* Larger than 64 bytes */
186 || pDosHdr->e_lfanew > 0x04000000UL /* or less that 64MB. */
187 )
188 && !*(PDWORD)paSections[0].ulAddress == IMAGE_NT_SIGNATURE
189 )
190 {
191 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: Not a pe2lx image!(?)\n"));
192 return FALSE;
193 }
194
195 /* Set pNtHdrs pointer. */
196 if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
197 pNtHdrs = (PIMAGE_NT_HEADERS)(paSections[0].ulAddress + pDosHdr->e_lfanew);
198 else
199 pNtHdrs = (PIMAGE_NT_HEADERS)paSections[0].ulAddress;
200
201 /* Validate the NT headers. */
202 if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE
203 || pNtHdrs->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC
204 || pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386
205 || (cSections != 1 /* all in one object */
206 && pNtHdrs->FileHeader.NumberOfSections
207 > cSections - 1 - (pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL ? 0 : 1) /* hdr section and stack */
208 )
209 /* TODO: add more tests? */
210 )
211 {
212 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: Not a pe2lx image!(?)\n"));
213 return FALSE;
214 }
215
216 /* set RVAs */
217 rc = setSectionRVAs();
218 if (rc != NO_ERROR)
219 {
220 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: setSectionRVAs failed with rc=%d\n", rc));
221 return FALSE;
222 }
223
224 /* Locate and set the entrypoint. */
225 setEntryPoint((ULONG)getPointerFromRVA(pNtHdrs->OptionalHeader.AddressOfEntryPoint));
226 if (entryPoint == 0UL &&
227 (pNtHdrs->OptionalHeader.AddressOfEntryPoint != NULL /* getPointerFromRVA failed... */
228 || !(pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL)) /* EXEs must have and entry point! */
229 )
230 {
231 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: entrypoint is incorrect, AddrOfEP=0x%08x, entryPoint=0x%08x\n",
232 pNtHdrs->OptionalHeader.AddressOfEntryPoint, entryPoint));
233 return FALSE;
234 }
235
236 /* Locate the resource directory (if any) */
237 if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE
238 && pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress > 0UL)
239 {
240 ulRVAResourceSection = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
241 pResDir = (PIMAGE_RESOURCE_DIRECTORY)getPointerFromRVA(ulRVAResourceSection);
242 }
243
244 /* TLS - Thread Local Storage */
245 if (pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress != 0UL
246 && pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size != 0UL)
247 {
248 PIMAGE_TLS_DIRECTORY pTLSDir;
249 pTLSDir = (PIMAGE_TLS_DIRECTORY)getPointerFromRVA(pNtHdrs->OptionalHeader.
250 DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].
251 VirtualAddress);
252
253 if (pTLSDir != NULL)
254 {
255 PVOID pv;
256 pv = getPointerFromRVA(pTLSDir->StartAddressOfRawData);
257 if (pv == NULL || pTLSDir->StartAddressOfRawData == 0UL)
258 {
259 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS StartAddressOfRawData - %#8x.\n",
260 pTLSDir->StartAddressOfRawData));
261 return FALSE;
262 }
263 setTLSAddress(pv);
264 setTLSInitSize(pTLSDir->EndAddressOfRawData - pTLSDir->StartAddressOfRawData);
265 setTLSTotalSize(pTLSDir->EndAddressOfRawData - pTLSDir->StartAddressOfRawData + pTLSDir->SizeOfZeroFill);
266 pv = getPointerFromRVA((ULONG)pTLSDir->AddressOfIndex);
267 if (pv == NULL)
268 {
269 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS AddressOffIndex - %#8x.\n",
270 pTLSDir->AddressOfIndex));
271 return FALSE;
272 }
273 setTLSIndexAddr((LPDWORD)pv);
274 pv = getPointerFromRVA((ULONG)pTLSDir->AddressOfCallBacks);
275 if (pv == NULL)
276 {
277 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS AddressOffIndex - %#8x.\n",
278 pTLSDir->AddressOfIndex));
279 return FALSE;
280 }
281 setTLSCallBackAddr((PIMAGE_TLS_CALLBACK*)pv);
282 }
283 else
284 {
285 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS Dir - %#8x. (getPointerFromRVA failed)\n",
286 pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress));
287 return FALSE;
288 }
289 }
290 return TRUE;
291}
292
293
294
295/**
296 * Gets the object layout for this module. Creates the paSections array.
297 * @returns OS2 errorcode. (NO_ERROR == success)
298 * @sketch Allocate output buffer.
299 * Call DosQuerySysState.
300 * IF buffer overflow THEN retry calling it with larger buffers, until buffer size is 1MB.
301 * IF success THEN
302 * BEGIN
303 * Find record for this module.
304 * IF record found THEN allocate memory for object table and copy it. (paSections, cSections)
305 * END
306 * @status Completely implemented.
307 * @author knut st. osmundsen
308 */
309ULONG Win32Pe2LxImage::getSections()
310{
311 APIRET rc = NO_ERROR;
312 qsGrec_t ** pBuf;
313 ULONG cbBuf = 65536;
314
315 pBuf = (qsGrec_t **)malloc(cbBuf);
316 if (pBuf != NULL)
317 {
318 rc = DosQuerySysState(QS_MTE, QS_MTE, getpid(), 0L, pBuf, cbBuf);
319 while (cbBuf < 1024*1024 && rc == ERROR_BUFFER_OVERFLOW)
320 {
321 PVOID pv = pBuf;
322 cbBuf += 65536;
323 pBuf = (qsGrec_t **)realloc(pv, cbBuf);
324 if (pBuf != NULL)
325 rc = DosQuerySysState(QS_MTE, QS_MTE, getpid(), 0L, pBuf, cbBuf);
326 else
327 {
328 rc = ERROR_NOT_ENOUGH_MEMORY;
329 free(pv);
330 }
331 }
332
333 if (rc == NO_ERROR)
334 {
335 qsGrec_t * pGrec = *pBuf;
336 qsLrec_t * pLrec = (qsLrec_t * )((ULONG)pGrec + sizeof(qsGrec_t));
337 while (pLrec != NULL && pLrec->hmte != hinstance)
338 pLrec = (qsLrec_t*)pLrec->pNextRec;
339
340 if (pLrec)
341 {
342 if (pLrec->pObjInfo != NULL)
343 {
344 /* Allocate memory for paSections */
345 paSections = (PSECTION)malloc(pLrec->ctObj*sizeof(SECTION));
346 if (paSections != NULL)
347 {
348 /* objects -> section array */
349 for (int i = 0; i < pLrec->ctObj; i++)
350 {
351 paSections[i].ulRVA = ~0UL;
352 paSections[i].cbVirtual = pLrec->pObjInfo[i].osize;
353 paSections[i].ulAddress = pLrec->pObjInfo[i].oaddr;
354 }
355 cSections = pLrec->ctObj;
356 }
357 else
358 rc = ERROR_NOT_ENOUGH_MEMORY;
359 }
360 else
361 {
362 rc = ERROR_BAD_EXE_FORMAT;
363 dprintf(("Win32Pe2LxImage::getSections: Error - no object table!\n"));
364 }
365 }
366 else
367 rc = ERROR_MOD_NOT_FOUND;
368 }
369 else
370 dprintf(("DosQuerySysState - failed with rc=%d (cbBuf=%d)\n", rc, cbBuf));
371
372 if (pBuf != NULL)
373 free(pBuf);
374 }
375 else
376 rc = ERROR_NOT_ENOUGH_MEMORY;
377
378 return rc;
379}
380
381
382/**
383 * Sets the ulRVA according to the original PE section table.
384 * @returns OS/2 errorcode. (NO_ERROR == success)
385 * @sketch DEBUG: Validate pNtHdrs
386 * Make pointer to start of PE section table.
387 * Set RVA for the header section.
388 * IF not all in one object exe? THEN
389 * Loop thru the sections in the PE section table.
390 * Note: due to the header section: PE section no. + 1 == LX object no.
391 * ELSE
392 * BEGIN
393 * (try) Reallocate paSections to NumberOfSections + 3.
394 * Loop thru the PE sections and make paSections from them.
395 * Add final Stack or TIBFix+Stack section if necessary.
396 * Resize header section.
397 * END
398 * @status completely implemented.
399 * @author knut st. osmundsen
400 * @remark Must not be called before pNtHdrs is set.
401 */
402ULONG Win32Pe2LxImage::setSectionRVAs()
403{
404 #if DEBUG
405 if (pNtHdrs == NULL)
406 {
407 DebugInt3();
408 return ERROR_INVALID_PARAMETER;
409 }
410 #endif
411
412 PIMAGE_SECTION_HEADER paPESections = (PIMAGE_SECTION_HEADER)
413 ((unsigned)pNtHdrs + sizeof(*pNtHdrs) +
414 (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes - IMAGE_NUMBEROF_DIRECTORY_ENTRIES) * sizeof(IMAGE_DATA_DIRECTORY)
415 );
416
417 /* set RVA for the header section to 0UL. */
418 paSections[0].ulRVA = 0UL;
419
420 /* All in one object exe? */
421 if (pNtHdrs->FileHeader.NumberOfSections < cSections)
422 {
423 /* loop thru the other sections */
424 for (int i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++)
425 paSections[i+1].ulRVA = paPESections[i].VirtualAddress;
426 }
427 else
428 { /* all in one object */
429 /* (try) Reallocate paSections to NumberOfSections + 3. */
430 PVOID pv = realloc(paSections, sizeof(paSections[0]) * (pNtHdrs->FileHeader.NumberOfSections + 3));
431 if (pv != NULL)
432 {
433 paSections = (PSECTION)pv;
434
435 /* loop thru the PE sections */
436 for (int i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++)
437 {
438 if (paSections[0].cbVirtual < paPESections[i].VirtualAddress)
439 {
440 dprintf(("Win32Pe2LxImage::setSectionRVAs: mismatch between section table and all-in-one-object"
441 "paSections[0].cbVirtual %#x paPESections[i].VirtualAddress %#x\n",
442 paSections[0].cbVirtual, paPESections[i].VirtualAddress
443 ));
444 return ERROR_BAD_EXE_FORMAT;
445 }
446 paSections[i+1].ulRVA = paPESections[i].VirtualAddress;
447 paSections[i+1].cbVirtual = max(paPESections[i].Misc.VirtualSize, paPESections[i].SizeOfRawData);
448 paSections[i+1].ulAddress = paSections[0].ulAddress + paPESections[i].VirtualAddress;
449 }
450 cSections = pNtHdrs->FileHeader.NumberOfSections + 1;
451
452 /* add final Stack or TIBFix+Stack section if necessary */
453 if (paSections[0].cbVirtual > paSections[i].ulRVA + ALIGN(paSections[i].cbVirtual, 0x1000))
454 {
455 paSections[i+1].ulRVA = ~0UL;
456 paSections[i+1].cbVirtual = paSections[0].cbVirtual - paSections[i].ulRVA + ALIGN(paSections[i].cbVirtual, 0x1000);
457 paSections[i+1].ulAddress = paSections[i].ulAddress + ALIGN(paSections[i].cbVirtual, 0x1000);
458 i++;
459 cSections++;
460 }
461
462 /* resize header section */
463 paSections[0].cbVirtual = paSections[1].ulRVA; /*....*/
464 }
465 else
466 return ERROR_NOT_ENOUGH_MEMORY;
467 }
468
469 return NO_ERROR;
470}
471
472
473/**
474 * Frees memory used by this object.
475 * When an exception is to be thrown, this function is called first to clean up
476 * the object. Base class(es) are automatically clean up by theire destructor(s).
477 * @status completely implemented.
478 * @author knut st. osmundsen
479 */
480VOID Win32Pe2LxImage::cleanup()
481{
482 if (paSections != NULL)
483 {
484 free(paSections);
485 paSections = NULL;
486 cSections = 0;
487 }
488}
489
490
491/**
492 * Converts a RVA to an pointer.
493 * @returns Pointer matching the given RVA, NULL on error.
494 * @param ulRVA An address relative to the imagebase of the original PE image.
495 * If this is 0UL NULL is returned.
496 * @sketch DEBUG: validate state, paSections != NULL
497 * IF ulRVA is 0 THEN return NULL
498 * LOOP while more section left and ulRVA is not within section
499 * next section
500 * IF section matching ulRVA is not found THEN fail.
501 * return pointer matching RVA.
502 * @status completely implemented.
503 * @author knut st. osmundsen
504 * @remark Should not be called until getSections has returned successfully.
505 * RVA == 0 is ignored.
506 */
507PVOID Win32Pe2LxImage::getPointerFromRVA(ULONG ulRVA)
508{
509 int i;
510 #ifdef DEBUG
511 if (paSections == NULL)
512 return NULL;
513 #endif
514
515 if (ulRVA == 0UL)
516 return NULL;
517
518 i = 0;
519 while (i < cSections &&
520 !(paSections[i].ulRVA <= ulRVA && paSections[i].ulRVA + paSections[i].cbVirtual > ulRVA)) /* ALIGN on page too? */
521 i++;
522
523 if (i >= cSections)
524 return NULL;
525
526 return (PVOID)(ulRVA - paSections[i].ulRVA + paSections[i].ulAddress);
527}
528
529/**
530 * Gets pointer to an exported procedure by procedure name.
531 * @returns Address of exported procedure. 0UL if not found.
532 * @param name Exported procedure name.
533 * @status completely implemented.
534 * @author Sander van Leeuwen
535 * @remark
536 */
537ULONG Win32Pe2LxImage::getApi(char *name)
538{
539 APIRET rc;
540 ULONG ulApiAddr;
541
542 rc = DosQueryProcAddr(hinstance, 0, name, (PFN *)&ulApiAddr);
543 return rc == NO_ERROR ? ulApiAddr : 0;
544}
545
546
547/**
548 * Gets pointer to an exported procedure by ordinal.
549 * @returns Pointer to an exported procedure. 0UL if not found.
550 * @param ordinal Export ordinal number.
551 * @status completely implemented.
552 * @author Sander van Leeuwen
553 * @remark FIXME:
554 * This function should be implemented for both Exe and Dll images!
555 * It could be done similar in both peldr image and pe2lx images by
556 * accessing PE structures.
557 */
558ULONG Win32Pe2LxImage::getApi(int ordinal)
559{
560 APIRET rc;
561 ULONG ulApiAddr;
562
563 rc = DosQueryProcAddr(hinstance, ordinal, NULL, (PFN *)&ulApiAddr);
564
565 return rc == NO_ERROR ? ulApiAddr : 0;
566}
Note: See TracBrowser for help on using the repository browser.