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

Last change on this file since 3430 was 3430, checked in by bird, 25 years ago

Corrected flaw in FP13 DosQuerySysState.

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