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

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

* empty log message *

File size: 19.4 KB
Line 
1/* $Id: winimagepe2lx.cpp,v 1.4 1999-11-16 18:27:01 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}
128
129
130/**
131 * Free memory associated with this object.
132 * @status completely implemented.
133 * @author knut st. osmundsen, Sander van Leeuwen
134 */
135Win32Pe2LxImage::~Win32Pe2LxImage()
136{
137 cleanup();
138}
139
140
141/**
142 * Initiates the object.
143 * Must be called immediately after the object construction.
144 * @returns Success indicator, TRUE == success; FALSE = failure.
145 * @sketch Get section placement and sizes for this module. (paSections, cSections)
146 * Verify that there is at least one section - the header section.
147 * Locate the NT headers.
148 * Set pNtHdrs pointer.
149 * Validate the NT headers.
150 * Read the PE section table the set the RVAs in paSections.
151 * Locate and set the entrypoint.
152 * Locate the resource directory (if any). (pResDir, pResourceSectionStart)
153 * TLS - FIXME!
154 * @status completely implemented.
155 * @author knut st. osmundsen
156 * @remark Object must be destroyed if failure!
157 */
158BOOL Win32Pe2LxImage::init()
159{
160 APIRET rc;
161
162 /* Get section placement and sizes for this module. (paSections, cSections) */
163 rc = getSections();
164 if (rc != NO_ERROR)
165 {
166 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: error - getSection failed with rc=%d\n",
167 rc));
168 return FALSE;
169 }
170
171 /* Verify that there is at least one section - the header section. */
172 if (cSections < 1)
173 {
174 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: no header section, cSections is 0\n"));
175 return FALSE;
176 }
177
178 /* Locate the NT headers. */
179 PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)paSections[0].ulAddress;
180 if ((pDosHdr->e_magic != IMAGE_DOS_SIGNATURE
181 || pDosHdr->e_lfanew < sizeof(IMAGE_DOS_HEADER) /* Larger than 64 bytes */
182 || pDosHdr->e_lfanew > 0x04000000UL /* or less that 64MB. */
183 )
184 && !*(PDWORD)paSections[0].ulAddress == IMAGE_NT_SIGNATURE
185 )
186 {
187 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: Not a pe2lx image!(?)\n"));
188 return FALSE;
189 }
190
191 /* Set pNtHdrs pointer. */
192 if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
193 pNtHdrs = (PIMAGE_NT_HEADERS)(paSections[0].ulAddress + pDosHdr->e_lfanew);
194 else
195 pNtHdrs = (PIMAGE_NT_HEADERS)paSections[0].ulAddress;
196
197 /* Validate the NT headers. */
198 if (pNtHdrs->Signature != IMAGE_NT_SIGNATURE
199 || pNtHdrs->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC
200 || pNtHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386
201 || (cSections != 1 /* all in one object */
202 && pNtHdrs->FileHeader.NumberOfSections
203 > cSections - 1 - (pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL ? 0 : 1) /* hdr section and stack */
204 )
205 /* TODO: add more tests? */
206 )
207 {
208 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: Not a pe2lx image!(?)\n"));
209 return FALSE;
210 }
211
212 /* set RVAs */
213 rc = setSectionRVAs();
214 if (rc != NO_ERROR)
215 {
216 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: setSectionRVAs failed with rc=%d\n", rc));
217 return FALSE;
218 }
219
220 /* Locate and set the entrypoint. */
221 setEntryPoint((ULONG)getPointerFromRVA(pNtHdrs->OptionalHeader.AddressOfEntryPoint));
222 if (entryPoint == 0UL &&
223 (pNtHdrs->OptionalHeader.AddressOfEntryPoint != NULL /* getPointerFromRVA failed... */
224 || !(pNtHdrs->FileHeader.Characteristics & IMAGE_FILE_DLL)) /* EXEs must have and entry point! */
225 )
226 {
227 dprintf(("Win32Pe2LxImage::Win32Pe2LxImage: entrypoint is incorrect, AddrOfEP=0x%08x, entryPoint=0x%08x\n",
228 pNtHdrs->OptionalHeader.AddressOfEntryPoint, entryPoint));
229 return FALSE;
230 }
231
232 /* Locate the resource directory (if any) */
233 if (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_RESOURCE
234 && pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress > 0UL)
235 {
236 pResourceSectionStart = pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
237 pResDir = (PIMAGE_RESOURCE_DIRECTORY)getPointerFromRVA(pResourceSectionStart);
238 }
239
240 /* TLS - Thread Local Storage */
241 if (pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress != 0UL
242 && pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size != 0UL)
243 {
244 PIMAGE_TLS_DIRECTORY pTLSDir;
245 pTLSDir = (PIMAGE_TLS_DIRECTORY)getPointerFromRVA(pNtHdrs->OptionalHeader.
246 DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].
247 VirtualAddress);
248
249 if (pTLSDir != NULL)
250 {
251 PVOID pv;
252 pv = getPointerFromRVA(pTLSDir->StartAddressOfRawData);
253 if (pv == NULL || pTLSDir->StartAddressOfRawData == 0UL)
254 {
255 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS StartAddressOfRawData - %#8x.\n",
256 pTLSDir->StartAddressOfRawData));
257 return FALSE;
258 }
259 setTLSAddress(pv);
260 setTLSInitSize(pTLSDir->EndAddressOfRawData - pTLSDir->StartAddressOfRawData);
261 setTLSTotalSize(pTLSDir->EndAddressOfRawData - pTLSDir->StartAddressOfRawData + pTLSDir->SizeOfZeroFill);
262 pv = getPointerFromRVA((ULONG)pTLSDir->AddressOfIndex);
263 if (pv == NULL)
264 {
265 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS AddressOffIndex - %#8x.\n",
266 pTLSDir->AddressOfIndex));
267 return FALSE;
268 }
269 setTLSIndexAddr((LPDWORD)pv);
270 pv = getPointerFromRVA((ULONG)pTLSDir->AddressOfCallBacks);
271 if (pv == NULL)
272 {
273 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS AddressOffIndex - %#8x.\n",
274 pTLSDir->AddressOfIndex));
275 return FALSE;
276 }
277 setTLSCallBackAddr((PIMAGE_TLS_CALLBACK*)pv);
278 }
279 else
280 {
281 eprintf(("Win32Pe2LxImage::init: invalid RVA to TLS Dir - %#8x. (getPointerFromRVA failed)\n",
282 pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress));
283 return FALSE;
284 }
285 }
286 return TRUE;
287}
288
289
290
291/**
292 * Gets the object layout for this module. Creates the paSections array.
293 * @returns OS2 errorcode. (NO_ERROR == success)
294 * @sketch Allocate output buffer.
295 * Call DosQuerySysState.
296 * IF buffer overflow THEN retry calling it with larger buffers, until buffer size is 1MB.
297 * IF success THEN
298 * BEGIN
299 * Find record for this module.
300 * IF record found THEN allocate memory for object table and copy it. (paSections, cSections)
301 * END
302 * @status Completely implemented.
303 * @author knut st. osmundsen
304 */
305ULONG Win32Pe2LxImage::getSections()
306{
307 APIRET rc = NO_ERROR;
308 qsGrec_t ** pBuf;
309 ULONG cbBuf = 65536;
310
311 pBuf = (qsGrec_t **)malloc(cbBuf);
312 if (pBuf != NULL)
313 {
314 rc = DosQuerySysState(QS_MTE, QS_MTE, getpid(), 0L, pBuf, cbBuf);
315 while (cbBuf < 1024*1024 && rc == ERROR_BUFFER_OVERFLOW)
316 {
317 PVOID pv = pBuf;
318 cbBuf += 65536;
319 pBuf = (qsGrec_t **)realloc(pv, cbBuf);
320 if (pBuf != NULL)
321 rc = DosQuerySysState(QS_MTE, QS_MTE, getpid(), 0L, pBuf, cbBuf);
322 else
323 {
324 rc = ERROR_NOT_ENOUGH_MEMORY;
325 free(pv);
326 }
327 }
328
329 if (rc == NO_ERROR)
330 {
331 qsGrec_t * pGrec = *pBuf;
332 qsLrec_t * pLrec = (qsLrec_t * )((ULONG)pGrec + sizeof(qsGrec_t));
333 while (pLrec != NULL && pLrec->hmte != hinstance)
334 pLrec = (qsLrec_t*)pLrec->pNextRec;
335
336 if (pLrec)
337 {
338 if (pLrec->pObjInfo != NULL)
339 {
340 /* Allocate memory for paSections */
341 paSections = (PSECTION)malloc(pLrec->ctObj*sizeof(SECTION));
342 if (paSections != NULL)
343 {
344 /* objects -> section array */
345 for (int i = 0; i < pLrec->ctObj; i++)
346 {
347 paSections[i].ulRVA = ~0UL;
348 paSections[i].cbVirtual = pLrec->pObjInfo[i].osize;
349 paSections[i].ulAddress = pLrec->pObjInfo[i].oaddr;
350 }
351 cSections = pLrec->ctObj;
352 }
353 else
354 rc = ERROR_NOT_ENOUGH_MEMORY;
355 }
356 else
357 {
358 rc = ERROR_BAD_EXE_FORMAT;
359 dprintf(("Win32Pe2LxImage::getSections: Error - no object table!\n"));
360 }
361 }
362 else
363 rc = ERROR_MOD_NOT_FOUND;
364 }
365 else
366 dprintf(("DosQuerySysState - failed with rc=%d (cbBuf=%d)\n", rc, cbBuf));
367
368 if (pBuf != NULL)
369 free(pBuf);
370 }
371 else
372 rc = ERROR_NOT_ENOUGH_MEMORY;
373
374 return rc;
375}
376
377
378/**
379 * Sets the ulRVA according to the original PE section table.
380 * @returns OS/2 errorcode. (NO_ERROR == success)
381 * @sketch DEBUG: Validate pNtHdrs
382 * Make pointer to start of PE section table.
383 * Set RVA for the header section.
384 * IF not all in one object exe? THEN
385 * Loop thru the sections in the PE section table.
386 * Note: due to the header section: PE section no. + 1 == LX object no.
387 * ELSE
388 * BEGIN
389 * (try) Reallocate paSections to NumberOfSections + 3.
390 * Loop thru the PE sections and make paSections from them.
391 * Add final Stack or TIBFix+Stack section if necessary.
392 * Resize header section.
393 * END
394 * @status completely implemented.
395 * @author knut st. osmundsen
396 * @remark Must not be called before pNtHdrs is set.
397 */
398ULONG Win32Pe2LxImage::setSectionRVAs()
399{
400 #if DEBUG
401 if (pNtHdrs == NULL)
402 {
403 DebugInt3();
404 return ERROR_INVALID_PARAMETER;
405 }
406 #endif
407
408 PIMAGE_SECTION_HEADER paPESections = (PIMAGE_SECTION_HEADER)
409 ((unsigned)pNtHdrs + sizeof(*pNtHdrs) +
410 (pNtHdrs->OptionalHeader.NumberOfRvaAndSizes - IMAGE_NUMBEROF_DIRECTORY_ENTRIES) * sizeof(IMAGE_DATA_DIRECTORY)
411 );
412
413 /* set RVA for the header section to 0UL. */
414 paSections[0].ulRVA = 0UL;
415
416 /* All in one object exe? */
417 if (pNtHdrs->FileHeader.NumberOfSections < cSections)
418 {
419 /* loop thru the other sections */
420 for (int i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++)
421 paSections[i+1].ulRVA = paPESections[i].VirtualAddress;
422 }
423 else
424 { /* all in one object */
425 /* (try) Reallocate paSections to NumberOfSections + 3. */
426 PVOID pv = realloc(paSections, sizeof(paSections[0]) * (pNtHdrs->FileHeader.NumberOfSections + 3));
427 if (pv != NULL)
428 {
429 paSections = (PSECTION)pv;
430
431 /* loop thru the PE sections */
432 for (int i = 0; i < pNtHdrs->FileHeader.NumberOfSections; i++)
433 {
434 if (paSections[0].cbVirtual < paPESections[i].VirtualAddress)
435 {
436 dprintf(("Win32Pe2LxImage::setSectionRVAs: mismatch between section table and all-in-one-object"
437 "paSections[0].cbVirtual %#x paPESections[i].VirtualAddress %#x\n",
438 paSections[0].cbVirtual, paPESections[i].VirtualAddress
439 ));
440 return ERROR_BAD_EXE_FORMAT;
441 }
442 paSections[i+1].ulRVA = paPESections[i].VirtualAddress;
443 paSections[i+1].cbVirtual = max(paPESections[i].Misc.VirtualSize, paPESections[i].SizeOfRawData);
444 paSections[i+1].ulAddress = paSections[0].ulAddress + paPESections[i].VirtualAddress;
445 }
446 cSections = pNtHdrs->FileHeader.NumberOfSections + 1;
447
448 /* add final Stack or TIBFix+Stack section if necessary */
449 if (paSections[0].cbVirtual > paSections[i].ulRVA + ALIGN(paSections[i].cbVirtual, 0x1000))
450 {
451 paSections[i+1].ulRVA = ~0UL;
452 paSections[i+1].cbVirtual = paSections[0].cbVirtual - paSections[i].ulRVA + ALIGN(paSections[i].cbVirtual, 0x1000);
453 paSections[i+1].ulAddress = paSections[i].ulAddress + ALIGN(paSections[i].cbVirtual, 0x1000);
454 i++;
455 cSections++;
456 }
457
458 /* resize header section */
459 paSections[0].cbVirtual = paSections[1].ulRVA; /*....*/
460 }
461 else
462 return ERROR_NOT_ENOUGH_MEMORY;
463 }
464
465 return NO_ERROR;
466}
467
468
469/**
470 * Frees memory used by this object.
471 * When an exception is to be thrown, this function is called first to clean up
472 * the object. Base class(es) are automatically clean up by theire destructor(s).
473 * @status completely implemented.
474 * @author knut st. osmundsen
475 */
476VOID Win32Pe2LxImage::cleanup()
477{
478 if (paSections != NULL)
479 {
480 free(paSections);
481 paSections = NULL;
482 cSections = 0;
483 }
484}
485
486
487/**
488 * Converts a RVA to an pointer.
489 * @returns Pointer matching the given RVA, NULL on error.
490 * @param ulRVA An address relative to the imagebase of the original PE image.
491 * If this is 0UL NULL is returned.
492 * @sketch DEBUG: validate state, paSections != NULL
493 * IF ulRVA is 0 THEN return NULL
494 * LOOP while more section left and ulRVA is not within section
495 * next section
496 * IF section matching ulRVA is not found THEN fail.
497 * return pointer matching RVA.
498 * @status completely implemented.
499 * @author knut st. osmundsen
500 * @remark Should not be called until getSections has returned successfully.
501 * RVA == 0 is ignored.
502 */
503PVOID Win32Pe2LxImage::getPointerFromRVA(ULONG ulRVA)
504{
505 int i;
506 #ifdef DEBUG
507 if (paSections == NULL)
508 return NULL;
509 #endif
510
511 if (ulRVA == 0UL)
512 return NULL;
513
514 i = 0;
515 while (i < cSections &&
516 !(paSections[i].ulRVA <= ulRVA && paSections[i].ulRVA + paSections[i].cbVirtual > ulRVA)) /* ALIGN on page too? */
517 i++;
518
519 if (i >= cSections)
520 return NULL;
521
522 return (PVOID)(ulRVA - paSections[i].ulRVA + paSections[i].ulAddress);
523}
524
Note: See TracBrowser for help on using the repository browser.