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

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

No exception throwing. Constructors is moved into an init method.
TLS is now implemented, but not tested.

File size: 19.2 KB
Line 
1/* $Id: winimagepe2lx.cpp,v 1.3 1999-10-17 01:49:09 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)
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 * @sketch DEBUG: validate state, paSections != NULL
492 * LOOP while more section left and ulRVA is not within section
493 * next section
494 * IF section matching ulRVA is not found THEN fail.
495 * return pointer matching RVA.
496 * @status completely implemented.
497 * @author knut st. osmundsen
498 * @remark Should not be called until getSections has returned successfully.
499 */
500PVOID Win32Pe2LxImage::getPointerFromRVA(ULONG ulRVA)
501{
502 int i;
503 #ifdef DEBUG
504 if (paSections == NULL)
505 return NULL;
506 #endif
507
508 i = 0;
509 while (i < cSections &&
510 !(paSections[i].ulRVA <= ulRVA && paSections[i].ulRVA + paSections[i].cbVirtual > ulRVA)) /* ALIGN on page too? */
511 i++;
512
513 if (i >= cSections)
514 return NULL;
515
516 return (PVOID)(ulRVA - paSections[i].ulRVA + paSections[i].ulAddress);
517}
518
Note: See TracBrowser for help on using the repository browser.