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

Last change on this file since 1767 was 1767, checked in by bird, 26 years ago

Hmm. Forgot to issue a call to setFullPath with szFileName during
object construction.

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