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

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

Implemented EnumResourceNamesA/W.

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