source: trunk/src/helpers/exeh.c@ 208

Last change on this file since 208 was 176, checked in by umoeller, 23 years ago

Added window list debugger.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 114.8 KB
Line 
1
2/*
3 *@@sourcefile exeh.c:
4 * contains code to load and parse executable headers
5 * and resources. See exehOpen for details.
6 *
7 * This file is new with V0.9.16 (2002-01-05) [umoeller]
8 * and contains code formerly in dosh2.c.
9 *
10 * Function prefixes:
11 * -- exe* executable helper functions.
12 *
13 * Note: Version numbering in this file relates to XWorkplace version
14 * numbering.
15 *
16 *@@header "helpers\exeh.h"
17 */
18
19/*
20 * This file Copyright (C) 2000-2002 Ulrich M”ller,
21 * Martin Lafaix.
22 * This file is part of the "XWorkplace helpers" source package.
23 * This is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published
25 * by the Free Software Foundation, in version 2 as it comes in the
26 * "COPYING" file of the XWorkplace main distribution.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 */
32
33#define OS2EMX_PLAIN_CHAR
34 // this is needed for "os2emx.h"; if this is defined,
35 // emx will define PSZ as _signed_ char, otherwise
36 // as unsigned char
37
38#define INCL_DOSMODULEMGR
39#define INCL_DOSPROCESS
40#define INCL_DOSEXCEPTIONS
41#define INCL_DOSSESMGR
42#define INCL_DOSQUEUES
43#define INCL_DOSMISC
44#define INCL_DOSDEVICES
45#define INCL_DOSDEVIOCTL
46#define INCL_DOSERRORS
47#include <os2.h>
48
49// #include <stdlib.h>
50#include <string.h>
51#include <stdio.h>
52#include <setjmp.h>
53
54#include "setup.h" // code generation and debugging options
55
56#include "helpers\dosh.h"
57#include "helpers\ensure.h"
58#include "helpers\except.h"
59#include "helpers\exeh.h"
60#include "helpers\standards.h"
61#include "helpers\stringh.h"
62
63#pragma hdrstop
64
65/*
66 *@@category: Helpers\Control program helpers\Executable info
67 * these functions can retrieve BLDLEVEL information,
68 * imported modules information, exported functions information,
69 * and resources information from any executable module. See
70 * exehOpen.
71 */
72
73/********************************************************************
74 *
75 * Executable functions
76 *
77 ********************************************************************/
78
79/*
80 *@@ exehOpen:
81 * this opens the specified executable file
82 * (which can be an .EXE, .COM, .DLL, or
83 * driver file) for use with the other
84 * exeh* functions.
85 *
86 * Basically this does a plain DosOpen on the
87 * executable file and reads in the various
88 * executable headers manually. Since DosLoadModule
89 * et al is never used, the OS/2 executable loader
90 * is completely circumvented. (Side note:
91 * DosLoadModule cannot be used on EXE files in
92 * the first place.)
93 *
94 * To be more precise, this uses doshOpen internally
95 * and can thus profit from the caching that is
96 * implemented there (V0.9.16).
97 *
98 * If no error occurs, NO_ERROR is returned
99 * and a pointer to a new EXECUTABLE structure
100 * is stored in *ppExec. Consider this pointer a
101 * handle and pass it to exehClose to clean up.
102 *
103 * If NO_ERROR is returned, all the fields through
104 * ulOS are set in EXECUTABLE. The psz* fields
105 * which follow afterwards require an additional
106 * call to exehQueryBldLevel.
107 *
108 * NOTE: If NO_ERROR is returned, the executable
109 * file is kept open by this function. It will
110 * only be closed when you call exehClose.
111 *
112 * If errors occur, this function returns the
113 * following error codes:
114 *
115 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
116 *
117 * -- ERROR_INVALID_EXE_SIGNATURE (191): header is
118 * neither plain DOS, nor NE, nor LX, nor PE.
119 * The given file probably isn't even an
120 * executable.
121 *
122 * -- ERROR_BAD_EXE_FORMAT (193): header was
123 * recognized, but the header data was
124 * not understood. Also this might be
125 * returned for .COM files which are
126 * not recognized.
127 *
128 * -- ERROR_INVALID_PARAMETER: ppExec is NULL.
129 *
130 * plus those of doshOpen and doshReadAt.
131 *
132 * The following executable types are supported
133 * (see EXECUTABLE for details):
134 *
135 * -- Plain DOS 3.x executable without new header.
136 *
137 * -- New Executable (NE), used by Win16 and
138 * 16-bit OS/2 and still many of today's drivers.
139 *
140 * -- Linear Executable (LX), OS/2 2.x and above.
141 *
142 * -- Portable Executable (PE), used by Win32.
143 *
144 * -- For files with the .COM, .BAT, or .CMD
145 * extensions, this will _not_ open the
146 * executable file, but set flags in EXECUTABLE
147 * only. Most importantly, EXECUTABLE.pDosExeHeader
148 * will be NULL.
149 *
150 * V0.9.12 adds support for NOSTUB executables,
151 * which are new-style executables (NE or LX)
152 * without a leading DOS header. JFS.IFS uses that
153 * format, for example. The executable then starts
154 * directly with the NE or LX header. I am not sure
155 * whether PE supports such beasts as well... if
156 * so, it should be supported too.
157 *
158 * Note that not all of the other exeh* functions
159 * support all of the executable types. See the
160 * respective function descriptions for remarks.
161 *
162 * @@todo:
163 *
164 * win95 \WINDOWS\extract.exe is NE with a non-standard format
165 * win16 \WINDOWS\EXPAND.EXE
166 * win16 \WINDOWS\MSD.EXE"
167 *
168 *@@added V0.9.0 [umoeller]
169 *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
170 *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
171 *@@changed V0.9.10 (2001-04-08) [lafaix]: added PE support
172 *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
173 *@@changed V0.9.12 (2001-05-03) [umoeller]: added support for NOSTUB newstyle executables
174 *@@changed V0.9.16 (2001-12-08) [umoeller]: now using OPEN_SHARE_DENYWRITE
175 *@@changed V0.9.16 (2001-12-08) [umoeller]: fLibrary was never set, works for LX, NE, and PE now
176 *@@changed V0.9.16 (2001-12-08) [umoeller]: speed optimizations, changed some return codes
177 *@@changed V0.9.16 (2002-01-04) [umoeller]: added fixes for COM, BAT, CMD extensions
178 */
179
180APIRET exehOpen(const char* pcszExecutable,
181 PEXECUTABLE* ppExec)
182{
183 APIRET arc = NO_ERROR;
184
185 PEXECUTABLE pExec = NULL;
186
187 PXFILE pFile = NULL;
188 ULONG cbFile = 0;
189 PCSZ pExt;
190 BOOL fOpenFile = FALSE;
191 BOOL fLoadNewHeader = FALSE;
192 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
193
194 if (!ppExec)
195 return (ERROR_INVALID_PARAMETER);
196
197 if (!(pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE))))
198 return (ERROR_NOT_ENOUGH_MEMORY);
199
200 memset(pExec, 0, sizeof(EXECUTABLE));
201
202 // check some of the default extensions
203 // V0.9.16 (2002-01-04) [umoeller]
204 if (pExt = doshGetExtension(pcszExecutable))
205 {
206 if (!stricmp(pExt, "COM"))
207 {
208 // I am not willing to find out more about the
209 // .COM executable format, so for this one case,
210 // let OS/2 determine what we have here
211 // (otherwise we do _not_ use DosQueryAppType
212 // because it's quite an expensive call)
213 ULONG ulDosAppType = 0;
214 if (!(arc = DosQueryAppType((PSZ)pcszExecutable, &ulDosAppType)))
215 {
216 if (ulDosAppType & FAPPTYP_DOS) // 0x20
217 pExec->ulOS = EXEOS_DOS3;
218 else
219 {
220 ULONG fl = ulDosAppType & FAPPTYP_WINDOWAPI; // 0x03
221 if ( (fl == FAPPTYP_WINDOWCOMPAT) // 0x02)
222 || (fl == FAPPTYP_NOTWINDOWCOMPAT) // 0x01)
223 )
224 pExec->ulOS = EXEOS_OS2;
225 else
226 arc = ERROR_BAD_EXE_FORMAT;
227 }
228
229 pExec->ulExeFormat = EXEFORMAT_COM;
230 }
231 }
232 else if (!stricmp(pExt, "BAT"))
233 {
234 pExec->ulOS = EXEOS_DOS3;
235 pExec->ulExeFormat = EXEFORMAT_TEXT_BATCH;
236 }
237 else if (!stricmp(pExt, "CMD"))
238 {
239 pExec->ulOS = EXEOS_OS2;
240 pExec->ulExeFormat = EXEFORMAT_TEXT_CMD;
241 }
242 else
243 fOpenFile = TRUE;
244 }
245
246 if ( (fOpenFile) // none of the above
247 && (!(arc = doshOpen((PSZ)pcszExecutable,
248 XOPEN_READ_EXISTING,
249 &cbFile,
250 &pFile)))
251 // file opened successfully:
252 )
253 {
254 pExec->pFile = pFile;
255 pExec->cbDosExeHeader = sizeof(DOSEXEHEADER);
256
257 // read old DOS EXE header
258 if (!(pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER))))
259 arc = ERROR_NOT_ENOUGH_MEMORY;
260 else if (!(arc = doshReadAt(pFile,
261 0,
262 &pExec->cbDosExeHeader, // in/out
263 (PBYTE)pExec->pDosExeHeader,
264 DRFL_FAILIFLESS)))
265 {
266 // now check if we really have a DOS header
267 if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
268 {
269 // arc = ERROR_INVALID_EXE_SIGNATURE;
270
271 // V0.9.12 (2001-05-03) [umoeller]
272 // try loading new header directly; there are
273 // drivers which were built with NOSTUB, and
274 // the exe image starts out with the NE or LX
275 // image directly (try JFS.IFS)
276 fLoadNewHeader = TRUE;
277 // ulNewHeaderOfs is 0 now
278
279 // remove the DOS header info, since we have none
280 // V0.9.12 (2001-05-03) [umoeller]
281 FREE(pExec->pDosExeHeader);
282 pExec->cbDosExeHeader = 0;
283 }
284 else
285 {
286 // we have a DOS header:
287 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
288 {
289 // neither LX nor PE nor NE:
290 pExec->ulOS = EXEOS_DOS3;
291 pExec->ulExeFormat = EXEFORMAT_OLDDOS;
292 }
293 else
294 {
295 // we have a new header offset:
296 fLoadNewHeader = TRUE;
297 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
298 }
299 }
300 }
301 }
302
303 if (fLoadNewHeader)
304 {
305 // either LX or PE or NE:
306 // read in new header...
307 // ulNewHeaderOfs is now either 0 (if no DOS header
308 // was found) or pDosExeHeader->ulNewHeaderOfs
309 // V0.9.12 (2001-05-03) [umoeller]
310
311 // read in the first two bytes to find out
312 // what extended header type we have; note,
313 // PE uses four bytes here
314 CHAR achNewHeaderType[4] = "???";
315 ULONG cbRead = 4;
316
317 if (!(arc = doshReadAt(pFile,
318 ulNewHeaderOfs,
319 &cbRead,
320 achNewHeaderType,
321 DRFL_FAILIFLESS)))
322 {
323 PBYTE pbCheckOS = NULL;
324
325 if (!memcmp(achNewHeaderType, "NE", 2))
326 {
327 // New Executable:
328 pExec->ulExeFormat = EXEFORMAT_NE;
329 cbRead = sizeof(NEHEADER);
330
331 // go read in the complete header then
332 // (doshReadAt has this in the cache)
333 if (!(pExec->pNEHeader = (PNEHEADER)malloc(cbRead)))
334 arc = ERROR_NOT_ENOUGH_MEMORY;
335 else if (!(arc = doshReadAt(pFile,
336 ulNewHeaderOfs,
337 &cbRead,
338 (PBYTE)pExec->pNEHeader,
339 0)))
340 {
341 if (cbRead < sizeof(NEHEADER))
342 arc = ERROR_BAD_EXE_FORMAT;
343 else
344 {
345 pExec->cbNEHeader = cbRead;
346 pbCheckOS = &pExec->pNEHeader->bTargetOS;
347 // set library flag V0.9.16 (2001-12-08) [umoeller]
348 if (pExec->pNEHeader->usFlags & 0x8000)
349 // library:
350 pExec->fLibrary = TRUE;
351 }
352 }
353 }
354 else if ( (!memcmp(achNewHeaderType, "LX", 2))
355 || (!memcmp(achNewHeaderType, "LE", 2))
356 // this is used by SMARTDRV.EXE
357 )
358 {
359 // OS/2 Linear Executable:
360 pExec->ulExeFormat = EXEFORMAT_LX;
361 cbRead = sizeof(LXHEADER);
362
363 // go read in the complete header then
364 // (doshReadAt has this in the cache)
365 if (!(pExec->pLXHeader = (PLXHEADER)malloc(cbRead)))
366 arc = ERROR_NOT_ENOUGH_MEMORY;
367 else if (!(arc = doshReadAt(pFile,
368 ulNewHeaderOfs,
369 &cbRead,
370 (PBYTE)pExec->pLXHeader,
371 0)))
372 {
373 if (cbRead < sizeof(LXHEADER))
374 arc = ERROR_BAD_EXE_FORMAT;
375 else
376 {
377 pExec->cbLXHeader = cbRead;
378 pbCheckOS = (PBYTE)(&pExec->pLXHeader->usTargetOS);
379 // set library flag V0.9.16 (2001-12-08) [umoeller]
380 if (pExec->pLXHeader->ulFlags & 0x8000)
381 // library:
382 pExec->fLibrary = TRUE;
383 }
384 }
385 }
386 else if (!memcmp(achNewHeaderType, "PE\0\0", 4))
387 {
388 pExec->ulExeFormat = EXEFORMAT_PE;
389
390 // PE has a standard header of 24 bytes
391 // plus an extended header, so check
392 // what we've got
393 if (!(pExec->pPEHeader = (PPEHEADER)malloc(sizeof(PEHEADER))))
394 arc = ERROR_NOT_ENOUGH_MEMORY;
395 else
396 {
397 ULONG ulOfs = ulNewHeaderOfs + 4;
398 PPEHEADER pPEHeader = pExec->pPEHeader;
399
400 // null the entire header
401 memset(pExec->pPEHeader,
402 0,
403 sizeof(PEHEADER));
404
405 // copy sig
406 pPEHeader->ulSignature = *((PULONG)&achNewHeaderType);
407
408 // read standard header
409 cbRead = sizeof(IMAGE_FILE_HEADER);
410 if (!(arc = doshReadAt(pFile,
411 ulOfs,
412 &cbRead,
413 (PBYTE)&pPEHeader->FileHeader,
414 0)))
415 {
416 if (cbRead < sizeof(IMAGE_FILE_HEADER))
417 // only if we don't even have the
418 // standard header, return an error
419 arc = ERROR_BAD_EXE_FORMAT;
420 else
421 {
422 pExec->f32Bits = TRUE;
423 pExec->cbPEHeader = 4 + sizeof(PEHEADER); // for now
424
425 if (pPEHeader->FileHeader.fsCharacteristics & IMAGE_FILE_DLL)
426 pExec->fLibrary = TRUE;
427
428 // try extended header
429 ulOfs += sizeof(IMAGE_FILE_HEADER);
430 if ( (cbRead = pPEHeader->FileHeader.usSizeOfOptionalHeader)
431 && (cbRead <= sizeof(IMAGE_OPTIONAL_HEADER))
432 )
433 {
434 if (!(arc = doshReadAt(pFile,
435 ulOfs,
436 &cbRead,
437 (PBYTE)&pPEHeader->OptionalHeader,
438 0)))
439 {
440 if (cbRead != sizeof(IMAGE_OPTIONAL_HEADER))
441 arc = ERROR_BAD_EXE_FORMAT;
442 else switch (pPEHeader->OptionalHeader.usSubsystem)
443 {
444 // case IMAGE_SUBSYSTEM_UNKNOWN: // 0
445 // case IMAGE_SUBSYSTEM_NATIVE: // 1
446 // case IMAGE_SUBSYSTEM_OS2_CUI: // 5
447 // case IMAGE_SUBSYSTEM_POSIX_CUI: // 7
448 // for these we shouldn't set win32
449
450 case IMAGE_SUBSYSTEM_WINDOWS_GUI: // 2 // Windows GUI subsystem
451 pExec->ulOS = EXEOS_WIN32_GUI;
452 break;
453
454 case IMAGE_SUBSYSTEM_WINDOWS_CUI: // 3 // Windows character subsystem
455 pExec->ulOS = EXEOS_WIN32_CLI;
456 break;
457 }
458
459 pExec->cbPEHeader = sizeof(PEHEADER);
460 }
461 }
462 } // end else if (cbRead < sizeof(IMAGE_FILE_HEADER))
463 } // end if (!(arc = doshReadAt(pFile,
464 } // end else if (!(pExec->pPEHeader = (PPEHEADER)malloc(sizeof(PEHEADER))))
465 } // end else if (!memcmp(achNewHeaderType, "PE\0\0", 4))
466 else
467 // strange type:
468 arc = ERROR_INVALID_EXE_SIGNATURE;
469
470 if ((!arc) && (pbCheckOS))
471 {
472 // BYTE to check for operating system
473 // (NE and LX):
474 switch (*pbCheckOS)
475 {
476 case NEOS_OS2:
477 pExec->ulOS = EXEOS_OS2;
478 if (pExec->ulExeFormat == EXEFORMAT_LX)
479 pExec->f32Bits = TRUE;
480 break;
481
482 case NEOS_WIN16:
483 pExec->ulOS = EXEOS_WIN16;
484 break;
485
486 case NEOS_DOS4:
487 pExec->ulOS = EXEOS_DOS4;
488 break;
489
490 case NEOS_WIN386:
491 pExec->ulOS = EXEOS_WIN386;
492 pExec->f32Bits = TRUE;
493 break;
494 }
495 }
496 } // end if (!(arc = doshReadAt(hFile,
497 } // end if (fLoadNewHeader)
498
499 if (arc != NO_ERROR)
500 // error: clean up
501 exehClose(&pExec);
502 else
503 *ppExec = pExec;
504
505 return arc;
506}
507
508/*
509 *@@ ParseBldLevel:
510 * called from exehQueryBldLevel to parse
511 * the BLDLEVEL string.
512 *
513 * On entry, caller has copied the string into
514 * pExec->pszDescription. The string is
515 * null-terminated.
516 *
517 * The BLDLEVEL string comes in two flavors.
518 *
519 * -- The standard format is:
520 *
521 + @#VENDOR:VERSION#@DESCRIPTION
522 *
523 * DESCRIPTION can have leading spaces, but
524 * need to have them.
525 *
526 * -- However, there is an extended version
527 * in that the DESCRIPTION field is split
528 * up even more. The marker for this seems
529 * to be that the description starts out
530 * with "##1##".
531 *
532 + ##1## DATETIME BUILDMACHINE:ASD:LANG:CTRY:REVISION:UNKNOWN:FIXPAK@@DESCRIPTION
533 *
534 * The problem is that the DATETIME field comes
535 * in several flavors. IBM uses things like
536 *
537 + "Thu Nov 30 15:30:37 2000 BWBLD228"
538 *
539 * while DANIS506.ADD has
540 *
541 + "15.12.2000 18:22:57 Nachtigall"
542 *
543 * Looks like the date/time string is standardized
544 * to have 24 characters then.
545 *
546 *@@added V0.9.12 (2001-05-18) [umoeller]
547 *@@changed V0.9.12 (2001-05-19) [umoeller]: added extended BLDLEVEL support
548 */
549
550static VOID ParseBldLevel(PEXECUTABLE pExec)
551{
552 PCSZ pStartOfVendor,
553 pStartOfInfo,
554 pEndOfVendor;
555
556 // @#VENDOR:VERSION#@ DESCRIPTION
557 // but skip the first byte, which has the string length
558 if ( (pStartOfVendor = strstr(pExec->pszDescription, "@#"))
559 && (pStartOfInfo = strstr(pStartOfVendor + 2, "#@"))
560 && (pEndOfVendor = strchr(pStartOfVendor + 2, ':'))
561 )
562 {
563 pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
564 pEndOfVendor);
565 pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
566 pStartOfInfo);
567 // skip "@#" in DESCRIPTION string
568 pStartOfInfo += 2;
569
570 // now check if we have extended DESCRIPTION V0.9.12 (2001-05-19) [umoeller]
571 if ( (strlen(pStartOfInfo) > 6)
572 && (!memcmp(pStartOfInfo, "##1##", 5))
573 )
574 {
575 // yes: parse that beast
576 const char *p = pStartOfInfo + 5;
577
578 // get build date/time
579 if (strlen(p) > 24)
580 {
581 // date/time seems to be fixed 24 chars in length
582 if (pExec->pszBuildDateTime = (PSZ)malloc(25))
583 {
584 memcpy(pExec->pszBuildDateTime,
585 p,
586 24);
587 pExec->pszBuildDateTime[24] = '\0';
588
589 p += 24;
590
591 // now we're at the colon-separated
592 // strings, first of which is the build machine;
593 // skip leading spaces
594 while (*p == ' ')
595 p++;
596
597 if (*p)
598 {
599 char **papsz[] =
600 {
601 &pExec->pszBuildMachine,
602 &pExec->pszASD,
603 &pExec->pszLanguage,
604 &pExec->pszCountry,
605 &pExec->pszRevision,
606 &pExec->pszUnknown,
607 &pExec->pszFixpak
608 };
609 ULONG ul;
610
611 for (ul = 0;
612 ul < sizeof(papsz) / sizeof(papsz[0]);
613 ul++)
614 {
615 BOOL fStop = FALSE;
616 const char *pNextColon = strchr(p, ':'),
617 *pDoubleAt = strstr(p, "@@");
618 if (!pNextColon)
619 {
620 // last item:
621 if (pDoubleAt)
622 pNextColon = pDoubleAt;
623 else
624 pNextColon = p + strlen(p);
625
626 fStop = TRUE;
627 }
628
629 if ( (fStop)
630 || ( (pNextColon)
631 && ( (!pDoubleAt)
632 || (pNextColon < pDoubleAt)
633 )
634 )
635 )
636 {
637 if (pNextColon > p + 1)
638 *(papsz[ul]) = strhSubstr(p, pNextColon);
639 }
640 else
641 break;
642
643 if (fStop)
644 break;
645
646 p = pNextColon + 1;
647 }
648 }
649 }
650 }
651
652 pStartOfInfo = strstr(p,
653 "@@");
654 if (pStartOfInfo)
655 pStartOfInfo += 2;
656 }
657
658 // -- if we had no extended DESCRIPTION,
659 // pStartOfInfo points to regular description now
660 // -- if we parse the extended DESCRIPTION above,
661 // pStartOfInfo points to after @@ now
662 // -- if we had an error, pStartOfInfo is NULL
663 if (pStartOfInfo)
664 {
665 // add the regular DESCRIPTION then
666 // skip leading spaces in info string
667 while (*pStartOfInfo == ' ')
668 pStartOfInfo++;
669 if (*pStartOfInfo) // V0.9.9 (2001-04-04) [umoeller]
670 // and copy until end of string
671 pExec->pszInfo = strdup(pStartOfInfo);
672 }
673 }
674}
675
676/*
677 *@@ exehQueryBldLevel:
678 * this retrieves buildlevel information for an
679 * LX or NE executable previously opened with
680 * exehOpen.
681 *
682 * BuildLevel information must be contained in the
683 * DESCRIPTION field of an executable's module
684 * definition (.DEF) file. In order to be readable
685 * by BLDLEVEL.EXE (which ships with OS/2), this
686 * string must have the following format:
687 *
688 + Description '@#AUTHOR:VERSION#@ DESCRIPTION'
689 *
690 * Example:
691 *
692 + Description '@#Ulrich M”ller:0.9.0#@ XWorkplace Sound Support Module'
693 *
694 * The "Description" entry always ends up as the
695 * very first entry in the non-resident name table
696 * in LX and NE executables. So this is what we retrieve
697 * here.
698 *
699 * If the first entry in that table exists, NO_ERROR is
700 * returned and at least the pszDescription field in
701 * EXECUTABLE is set to that information.
702 *
703 * If that string is in IBM BLDLEVEL format, the string
704 * is automatically parsed, and the pszVendor, pszVersion,
705 * and pszInfo fields are also set. In the above examples,
706 * this would return the following information:
707 *
708 + pszVendor = "Ulrich M”ller"
709 + pszVersion = "0.9.0"
710 + pszInfo = "XWorkplace Sound Support Module"
711 *
712 * See ParseBldLevel for extended formats.
713 *
714 * If that string is not in BLDLEVEL format, only
715 * pszDescription will be set. The other fields remain
716 * NULL. Still, NO_ERROR is returned.
717 *
718 * This returns the following errors:
719 *
720 * -- ERROR_INVALID_PARAMETER: pExec is NULL.
721 *
722 * -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in
723 * LX or NE format.
724 *
725 * -- ERROR_INVALID_DATA (13): non-resident name table not found,
726 * or table is empty.
727 *
728 * -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
729 *
730 * plus the error codes of doshReadAt.
731 *
732 *@@added V0.9.0 [umoeller]
733 *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
734 *@@changed V0.9.1 (99-12-06): fixed memory leak
735 *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
736 *@@changed V0.9.12 (2001-05-18) [umoeller]: extracted ParseBldLevel
737 *@@changed V0.9.16 (2002-01-05) [umoeller]: optimizations
738 */
739
740APIRET exehQueryBldLevel(PEXECUTABLE pExec)
741{
742 APIRET arc = NO_ERROR;
743
744 if (!pExec)
745 arc = ERROR_INVALID_PARAMETER;
746 else
747 {
748 PXFILE pFile = pExec->pFile;
749
750 ULONG ulNRNTOfs = 0;
751
752 if (pExec->ulExeFormat == EXEFORMAT_LX)
753 {
754 // OK, LX format:
755 // check if we have a non-resident name table
756 if (pExec->pLXHeader == NULL)
757 arc = ERROR_INVALID_DATA;
758 else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
759 arc = ERROR_INVALID_DATA;
760 else
761 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
762 }
763 else if (pExec->ulExeFormat == EXEFORMAT_NE)
764 {
765 // OK, NE format:
766 // check if we have a non-resident name table
767 if (pExec->pNEHeader == NULL)
768 arc = ERROR_INVALID_DATA;
769 else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
770 arc = ERROR_INVALID_DATA;
771 else
772 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
773 }
774 else
775 // neither LX nor NE: stop
776 arc = ERROR_INVALID_EXE_SIGNATURE;
777
778 if ( (!arc)
779 && (ulNRNTOfs)
780 )
781 {
782 ULONG cb = 2000;
783
784 PSZ pszNameTable;
785
786 if (!(pszNameTable = (PSZ)malloc(2001)))
787 arc = ERROR_NOT_ENOUGH_MEMORY;
788 else
789 {
790 // V0.9.16 (2002-01-05) [umoeller]: rewrote the following
791
792 // read from offset of non-resident name table
793 if (!(arc = doshReadAt(pFile, // file is still open
794 ulNRNTOfs, // ofs determined above
795 &cb, // 2000
796 pszNameTable,
797 0)))
798 {
799 // the string is in Pascal format, so the
800 // first byte has the length
801 BYTE bLen;
802 if (!(bLen = *pszNameTable))
803 // length byte is null:
804 arc = ERROR_INVALID_DATA;
805 else
806 {
807 // now copy the string
808 if (!(pExec->pszDescription = (PSZ)malloc(bLen + 1)))
809 arc = ERROR_NOT_ENOUGH_MEMORY;
810 else
811 {
812 memcpy(pExec->pszDescription,
813 pszNameTable + 1, // skip length byte
814 bLen); // length byte
815 // terminate string
816 pExec->pszDescription[bLen] = 0;
817
818 ParseBldLevel(pExec);
819 }
820 }
821 }
822
823 free(pszNameTable);
824 }
825 }
826 } // end if (!pExec)
827
828 return arc;
829}
830
831/*
832 *@@ exehQueryImportedModules:
833 * returns an array of FSYSMODULE structure describing all
834 * imported modules.
835 *
836 * *pcModules receives the # of items in the array (not the
837 * array size!). Use doshFreeImportedModules to clean up.
838 *
839 * This returns a standard OS/2 error code, which might be
840 * any of the codes returned by DosSetFilePtr and DosRead.
841 * In addition, this may return:
842 *
843 * -- ERROR_NOT_ENOUGH_MEMORY
844 *
845 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
846 * than LX or NE, which is not understood by this function.
847 *
848 * Even if NO_ERROR is returned, the array pointer might still
849 * be NULL if the module contains no such data.
850 *
851 *@@added V0.9.9 (2001-03-11) [lafaix]
852 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
853 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
854 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
855 *@@changed V0.9.10 (2001-04-13) [lafaix]: removed 127 characters limit
856 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
857 */
858
859APIRET exehQueryImportedModules(PEXECUTABLE pExec,
860 PFSYSMODULE *ppaModules, // out: modules array
861 PULONG pcModules) // out: array item count
862{
863 if ( (pExec)
864 && ( (pExec->ulOS == EXEOS_OS2)
865 || (pExec->ulOS == EXEOS_WIN16)
866 || (pExec->ulOS == EXEOS_WIN386)
867 )
868 )
869 {
870 ENSURE_BEGIN;
871 ULONG cModules = 0;
872 PFSYSMODULE paModules = NULL;
873 int i;
874 HFILE hfExe = pExec->pFile->hf;
875
876 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
877
878 if (pExec->pDosExeHeader)
879 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
880 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
881
882 if (pExec->ulExeFormat == EXEFORMAT_LX)
883 {
884 // 32-bit OS/2 executable:
885 cModules = pExec->pLXHeader->ulImportModTblCnt;
886
887 if (cModules)
888 {
889 ULONG cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
890 ULONG ulDummy;
891
892 paModules = (PFSYSMODULE)malloc(cb);
893 if (!paModules)
894 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
895
896 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
897
898 ENSURE_SAFE(DosSetFilePtr(hfExe,
899 pExec->pLXHeader->ulImportModTblOfs
900 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
901 FILE_BEGIN,
902 &ulDummy));
903
904 for (i = 0; i < cModules; i++)
905 {
906 BYTE bLen = 0;
907
908 // reading the length of the module name
909 ENSURE_SAFE(DosRead(hfExe, &bLen, 1, &ulDummy));
910
911 // reading the module name
912 ENSURE_SAFE(DosRead(hfExe,
913 paModules[i].achModuleName,
914 bLen,
915 &ulDummy));
916
917 // module names are not null terminated, so we must
918 // do it now
919 paModules[i].achModuleName[bLen] = 0;
920 } // end for
921 }
922 } // end LX
923 else if (pExec->ulExeFormat == EXEFORMAT_NE)
924 {
925 // 16-bit executable:
926 cModules = pExec->pNEHeader->usModuleTblEntries;
927
928 if (cModules)
929 {
930 ULONG cb = sizeof(FSYSMODULE) * cModules;
931
932 paModules = (PFSYSMODULE)malloc(cb);
933 if (!paModules)
934 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
935
936 memset(paModules, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
937
938 for (i = 0; i < cModules; i ++)
939 {
940 BYTE bLen;
941 USHORT usOfs;
942 ULONG ulDummy;
943
944 // the module reference table contains offsets
945 // relative to the import table; we hence read
946 // the offset in the module reference table, and
947 // then we read the name in the import table
948
949 ENSURE_SAFE(DosSetFilePtr(hfExe,
950 pExec->pNEHeader->usModRefTblOfs
951 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
952 + sizeof(usOfs) * i,
953 FILE_BEGIN,
954 &ulDummy));
955
956 ENSURE_SAFE(DosRead(hfExe, &usOfs, 2, &ulDummy));
957
958 ENSURE_SAFE(DosSetFilePtr(hfExe,
959 pExec->pNEHeader->usImportTblOfs
960 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
961 + usOfs,
962 FILE_BEGIN,
963 &ulDummy));
964
965 ENSURE_SAFE(DosRead(hfExe, &bLen, 1, &ulDummy));
966
967 ENSURE_SAFE(DosRead(hfExe,
968 paModules[i].achModuleName,
969 bLen,
970 &ulDummy));
971
972 paModules[i].achModuleName[bLen] = 0;
973 } // end for
974 }
975 } // end NE
976 else
977 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
978
979 // no error: output data
980 *ppaModules = paModules;
981 *pcModules = cModules;
982
983 ENSURE_FINALLY;
984 // if we had an error above, clean up
985 free(paModules);
986 ENSURE_END;
987 }
988 else
989 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
990
991 ENSURE_OK;
992}
993
994/*
995 *@@ exehFreeImportedModules:
996 * frees resources allocated by exehQueryImportedModules.
997 *
998 *@@added V0.9.9 (2001-03-11)
999 */
1000
1001APIRET exehFreeImportedModules(PFSYSMODULE paModules)
1002{
1003 free(paModules);
1004 return NO_ERROR;
1005}
1006
1007/*
1008 *@@ ScanLXEntryTable:
1009 * returns the number of exported entries in the entry table.
1010 *
1011 * If paFunctions is not NULL, then successive entries are
1012 * filled with the found type and ordinal values.
1013 *
1014 *@@added V0.9.9 (2001-03-30) [lafaix]
1015 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1016 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1017 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1018 */
1019
1020static APIRET ScanLXEntryTable(PEXECUTABLE pExec,
1021 PFSYSFUNCTION paFunctions,
1022 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1023{
1024 ULONG ulDummy;
1025 USHORT usOrdinal = 1,
1026 usCurrent = 0;
1027 int i;
1028
1029 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1030 HFILE hfExe = pExec->pFile->hf;
1031
1032 if (pExec->pDosExeHeader)
1033 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1034 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1035
1036 ENSURE(DosSetFilePtr(hfExe,
1037 pExec->pLXHeader->ulEntryTblOfs
1038 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1039 FILE_BEGIN,
1040 &ulDummy));
1041
1042 while (TRUE)
1043 {
1044 BYTE bCnt,
1045 bType,
1046 bFlag;
1047
1048 ENSURE(DosRead(hfExe, &bCnt, 1, &ulDummy));
1049
1050 if (bCnt == 0)
1051 // end of the entry table
1052 break;
1053
1054 ENSURE(DosRead(hfExe, &bType, 1, &ulDummy));
1055
1056 switch (bType & 0x7F)
1057 {
1058 /*
1059 * unused entries
1060 *
1061 */
1062
1063 case 0:
1064 usOrdinal += bCnt;
1065 break;
1066
1067 /*
1068 * 16-bit entries
1069 *
1070 * the bundle type is followed by the object number
1071 * and by bCnt bFlag+usOffset entries
1072 *
1073 */
1074
1075 case 1:
1076 ENSURE(DosSetFilePtr(hfExe,
1077 sizeof(USHORT),
1078 FILE_CURRENT,
1079 &ulDummy));
1080
1081 for (i = 0; i < bCnt; i ++)
1082 {
1083 ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
1084
1085 if (bFlag & 0x01)
1086 {
1087 if (paFunctions)
1088 {
1089 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1090 paFunctions[usCurrent].ulType = 1;
1091 paFunctions[usCurrent].achFunctionName[0] = 0;
1092 }
1093 usCurrent++;
1094 }
1095
1096 usOrdinal++;
1097
1098 ENSURE(DosSetFilePtr(hfExe,
1099 sizeof(USHORT),
1100 FILE_CURRENT,
1101 &ulDummy));
1102
1103 } // end for
1104 break;
1105
1106 /*
1107 * 286 call gate entries
1108 *
1109 * the bundle type is followed by the object number
1110 * and by bCnt bFlag+usOffset+usCallGate entries
1111 *
1112 */
1113
1114 case 2:
1115 ENSURE(DosSetFilePtr(hfExe,
1116 sizeof(USHORT),
1117 FILE_CURRENT,
1118 &ulDummy));
1119
1120 for (i = 0; i < bCnt; i ++)
1121 {
1122 ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
1123
1124 if (bFlag & 0x01)
1125 {
1126 if (paFunctions)
1127 {
1128 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1129 paFunctions[usCurrent].ulType = 2;
1130 paFunctions[usCurrent].achFunctionName[0] = 0;
1131 }
1132 usCurrent++;
1133 }
1134
1135 usOrdinal++;
1136
1137 ENSURE(DosSetFilePtr(hfExe,
1138 sizeof(USHORT) + sizeof(USHORT),
1139 FILE_CURRENT,
1140 &ulDummy));
1141
1142 } // end for
1143 break;
1144
1145 /*
1146 * 32-bit entries
1147 *
1148 * the bundle type is followed by the object number
1149 * and by bCnt bFlag+ulOffset entries
1150 *
1151 */
1152
1153 case 3:
1154 ENSURE(DosSetFilePtr(hfExe,
1155 sizeof(USHORT),
1156 FILE_CURRENT,
1157 &ulDummy));
1158
1159 for (i = 0; i < bCnt; i ++)
1160 {
1161 ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
1162
1163 if (bFlag & 0x01)
1164 {
1165 if (paFunctions)
1166 {
1167 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1168 paFunctions[usCurrent].ulType = 3;
1169 paFunctions[usCurrent].achFunctionName[0] = 0;
1170 }
1171 usCurrent++;
1172 }
1173
1174 usOrdinal++;
1175
1176 ENSURE(DosSetFilePtr(hfExe,
1177 sizeof(ULONG),
1178 FILE_CURRENT,
1179 &ulDummy));
1180 } // end for
1181 break;
1182
1183 /*
1184 * forwarder entries
1185 *
1186 * the bundle type is followed by a reserved word
1187 * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
1188 *
1189 */
1190
1191 case 4:
1192 ENSURE(DosSetFilePtr(hfExe,
1193 sizeof(USHORT),
1194 FILE_CURRENT,
1195 &ulDummy));
1196
1197 for (i = 0; i < bCnt; i ++)
1198 {
1199 ENSURE(DosSetFilePtr(hfExe,
1200 sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
1201 FILE_CURRENT,
1202 &ulDummy));
1203
1204 if (paFunctions)
1205 {
1206 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1207 paFunctions[usCurrent].ulType = 4;
1208 paFunctions[usCurrent].achFunctionName[0] = 0;
1209 }
1210 usCurrent++;
1211
1212 usOrdinal++;
1213 } // end for
1214 break;
1215
1216 /*
1217 * unknown bundle type
1218 *
1219 * we don't know how to handle this bundle, so we must
1220 * stop parsing the entry table here (as we don't know the
1221 * bundle size); if paFunctions is not null, we fill it with
1222 * informative data
1223 */
1224
1225 default:
1226 if (paFunctions)
1227 {
1228 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1229 paFunctions[usCurrent].ulType = bType;
1230 sprintf(paFunctions[usCurrent].achFunctionName,
1231 "Unknown bundle type encountered (%d). Aborting entry table scan.",
1232 bType);
1233
1234 usCurrent++;
1235 }
1236 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
1237 // whatever
1238 // V0.9.9 (2001-04-03) [umoeller]
1239 } // end switch (bType & 0x7F)
1240 } // end while (TRUE)
1241
1242 if (pcEntries)
1243 *pcEntries = usCurrent;
1244
1245 ENSURE_OK;
1246}
1247
1248/*
1249 *@@ ScanNEEntryTable:
1250 * returns the number of exported entries in the entry table.
1251 *
1252 * if paFunctions is not NULL, then successive entries are
1253 * filled with the found type and ordinal values.
1254 *
1255 *@@added V0.9.9 (2001-03-30) [lafaix]
1256 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1257 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1258 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1259 */
1260
1261static APIRET ScanNEEntryTable(PEXECUTABLE pExec,
1262 PFSYSFUNCTION paFunctions,
1263 PULONG pcEntries) // out: entry table entry count; ptr can be NULL
1264{
1265 ULONG ulDummy;
1266 USHORT usOrdinal = 1,
1267 usCurrent = 0;
1268 int i;
1269
1270 ULONG ulNewHeaderOfs = 0;
1271 HFILE hfExe = pExec->pFile->hf;
1272
1273 if (pExec->pDosExeHeader)
1274 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1275 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1276
1277 ENSURE(DosSetFilePtr(hfExe,
1278 pExec->pNEHeader->usEntryTblOfs
1279 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1280 FILE_BEGIN,
1281 &ulDummy));
1282
1283 while (TRUE)
1284 {
1285 BYTE bCnt,
1286 bType,
1287 bFlag;
1288
1289 ENSURE(DosRead(hfExe, &bCnt, 1, &ulDummy));
1290
1291 if (bCnt == 0)
1292 // end of the entry table
1293 break;
1294
1295 ENSURE(DosRead(hfExe, &bType, 1, &ulDummy));
1296
1297 if (bType)
1298 {
1299 for (i = 0; i < bCnt; i++)
1300 {
1301 ENSURE(DosRead(hfExe,
1302 &bFlag,
1303 1,
1304 &ulDummy));
1305
1306 if (bFlag & 0x01)
1307 {
1308 if (paFunctions)
1309 {
1310 paFunctions[usCurrent].ulOrdinal = usOrdinal;
1311 paFunctions[usCurrent].ulType = 1; // 16-bit entry
1312 paFunctions[usCurrent].achFunctionName[0] = 0;
1313 }
1314 usCurrent++;
1315 }
1316
1317 usOrdinal++;
1318
1319 if (bType == 0xFF)
1320 {
1321 // moveable segment
1322 ENSURE(DosSetFilePtr(hfExe,
1323 5,
1324 FILE_CURRENT,
1325 &ulDummy));
1326 }
1327 else
1328 {
1329 // fixed segment or constant (0xFE)
1330 ENSURE(DosSetFilePtr(hfExe,
1331 2,
1332 FILE_CURRENT,
1333 &ulDummy));
1334 }
1335
1336 } // end for
1337 }
1338 else
1339 usOrdinal += bCnt;
1340 } // end while (TRUE)
1341
1342 if (pcEntries)
1343 *pcEntries = usCurrent;
1344
1345 ENSURE_OK;
1346}
1347
1348/*
1349 *@@ Compare:
1350 * binary search helper
1351 *
1352 *@@added V0.9.9 (2001-04-01) [lafaix]
1353 *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
1354 */
1355
1356static int _Optlink Compare(const void *key,
1357 const void *element)
1358{
1359 USHORT usOrdinal = *((PUSHORT) key);
1360 PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
1361
1362 if (usOrdinal > pFunction->ulOrdinal)
1363 return (1);
1364 else if (usOrdinal < pFunction->ulOrdinal)
1365 return (-1);
1366 else
1367 return (0);
1368}
1369
1370/*
1371 *@@ ScanNameTable:
1372 * scans a resident or non-resident name table, and fills the
1373 * appropriate paFunctions entries when it encounters exported
1374 * entries names.
1375 *
1376 * This functions works for both NE and LX executables.
1377 *
1378 *@@added V0.9.9 (2001-03-30) [lafaix]
1379 *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
1380 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1381 *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
1382 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1383 */
1384
1385static APIRET ScanNameTable(PEXECUTABLE pExec,
1386 ULONG cFunctions,
1387 PFSYSFUNCTION paFunctions)
1388{
1389 ULONG ulDummy;
1390
1391 USHORT usOrdinal;
1392 PFSYSFUNCTION pFunction;
1393 HFILE hfExe = pExec->pFile->hf;
1394
1395 while (TRUE)
1396 {
1397 BYTE bLen;
1398 CHAR achName[256];
1399 // int i;
1400
1401 ENSURE(DosRead(hfExe, &bLen, 1, &ulDummy));
1402
1403 if (bLen == 0)
1404 // end of the name table
1405 break;
1406
1407 ENSURE(DosRead(hfExe, &achName, bLen, &ulDummy));
1408 achName[bLen] = 0;
1409
1410 ENSURE(DosRead(hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
1411
1412 if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
1413 paFunctions,
1414 cFunctions,
1415 sizeof(FSYSFUNCTION),
1416 Compare)))
1417 {
1418 memcpy(pFunction->achFunctionName,
1419 achName,
1420 bLen+1);
1421 }
1422 }
1423
1424 ENSURE_OK;
1425}
1426
1427/*
1428 *@@ exehQueryExportedFunctions:
1429 * returns an array of FSYSFUNCTION structure describing all
1430 * exported functions.
1431 *
1432 * *pcFunctions receives the # of items in the array (not the
1433 * array size!). Use doshFreeExportedFunctions to clean up.
1434 *
1435 * Note that the returned array only contains entry for exported
1436 * functions. Empty export entries are _not_ included.
1437 *
1438 * This returns a standard OS/2 error code, which might be
1439 * any of the codes returned by DosSetFilePtr and DosRead.
1440 * In addition, this may return:
1441 *
1442 * -- ERROR_NOT_ENOUGH_MEMORY
1443 *
1444 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1445 * than LX or NE, which is not understood by this function.
1446 *
1447 * -- If ERROR_INVALID_LIST_FORMAT is returned, the format of an
1448 * export entry wasn't understood here.
1449 *
1450 * Even if NO_ERROR is returned, the array pointer might still
1451 * be NULL if the module contains no such data.
1452 *
1453 *@@added V0.9.9 (2001-03-11) [lafaix]
1454 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1455 *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
1456 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1457 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1458 */
1459
1460APIRET exehQueryExportedFunctions(PEXECUTABLE pExec,
1461 PFSYSFUNCTION *ppaFunctions, // out: functions array
1462 PULONG pcFunctions) // out: array item count
1463{
1464 if ( (pExec)
1465 && ( (pExec->ulOS == EXEOS_OS2)
1466 || (pExec->ulOS == EXEOS_WIN16)
1467 || (pExec->ulOS == EXEOS_WIN386)
1468 )
1469 )
1470 {
1471 ENSURE_BEGIN;
1472 ULONG cFunctions = 0;
1473 PFSYSFUNCTION paFunctions = NULL;
1474
1475 ULONG ulDummy;
1476
1477 HFILE hfExe = pExec->pFile->hf;
1478 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1479
1480 if (pExec->pDosExeHeader)
1481 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1482 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1483
1484 if (pExec->ulExeFormat == EXEFORMAT_LX)
1485 {
1486 // It's a 32bit OS/2 executable
1487
1488 // the number of exported entry points is not stored
1489 // in the executable header; we have to count them in
1490 // the entry table
1491
1492 ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
1493
1494 // we now have the number of exported entries; let us
1495 // build them
1496
1497 if (cFunctions)
1498 {
1499 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
1500
1501 paFunctions = (PFSYSFUNCTION)malloc(cb);
1502 if (!paFunctions)
1503 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1504
1505 // we rescan the entry table (the cost is not as bad
1506 // as it may seem, due to disk caching)
1507
1508 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
1509
1510 // we now scan the resident name table entries
1511
1512 ENSURE_SAFE(DosSetFilePtr(hfExe,
1513 pExec->pLXHeader->ulResdNameTblOfs
1514 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1515 FILE_BEGIN,
1516 &ulDummy));
1517
1518 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1519
1520 // we now scan the non-resident name table entries,
1521 // whose offset is _from the begining of the file_
1522
1523 ENSURE_SAFE(DosSetFilePtr(hfExe,
1524 pExec->pLXHeader->ulNonResdNameTblOfs,
1525 FILE_BEGIN,
1526 &ulDummy));
1527
1528 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1529 } // end if (cFunctions)
1530 }
1531 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1532 {
1533 // it's a "new" segmented 16bit executable
1534
1535 // here too the number of exported entry points
1536 // is not stored in the executable header; we
1537 // have to count them in the entry table
1538
1539 ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
1540
1541 // we now have the number of exported entries; let us
1542 // build them
1543
1544 if (cFunctions)
1545 {
1546 // USHORT usOrdinal = 1;
1547 // usCurrent = 0;
1548
1549 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
1550 if (!paFunctions)
1551 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1552
1553 // we rescan the entry table (the cost is not as bad
1554 // as it may seem, due to disk caching)
1555
1556 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
1557
1558 // we now scan the resident name table entries
1559
1560 ENSURE_SAFE(DosSetFilePtr(hfExe,
1561 pExec->pNEHeader->usResdNameTblOfs
1562 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1563 FILE_BEGIN,
1564 &ulDummy));
1565
1566 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1567
1568 // we now scan the non-resident name table entries,
1569 // whose offset is _from the begining of the file_
1570
1571 ENSURE_SAFE(DosSetFilePtr(hfExe,
1572 pExec->pNEHeader->ulNonResdTblOfs,
1573 FILE_BEGIN,
1574 &ulDummy));
1575
1576 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
1577 }
1578 }
1579 else
1580 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1581
1582 // no error: output data
1583 *ppaFunctions = paFunctions;
1584 *pcFunctions = cFunctions;
1585
1586 ENSURE_FINALLY;
1587 // if we had an error above, clean up
1588 free(paFunctions);
1589 ENSURE_END;
1590 }
1591 else
1592 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1593
1594 ENSURE_OK;
1595}
1596
1597/*
1598 *@@ exehFreeExportedFunctions:
1599 * frees resources allocated by exehQueryExportedFunctions.
1600 *
1601 *@@added V0.9.9 (2001-03-11)
1602 */
1603
1604APIRET exehFreeExportedFunctions(PFSYSFUNCTION paFunctions)
1605{
1606 free(paFunctions);
1607
1608 return NO_ERROR;
1609}
1610
1611/*
1612 *@@ exehQueryResources:
1613 * returns an array of FSYSRESOURCE structures describing all
1614 * available resources in the module.
1615 *
1616 * *pcResources receives the no. of items in the array
1617 * (not the array size!). Use exehFreeResources to clean up.
1618 *
1619 * This returns a standard OS/2 error code, which might be
1620 * any of the codes returned by DosSetFilePtr and DosRead.
1621 * In addition, this may return:
1622 *
1623 * -- ERROR_NOT_ENOUGH_MEMORY
1624 *
1625 * -- ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
1626 * than LX or NE, which is not understood by this function.
1627 *
1628 * Even if NO_ERROR is returned, the array pointer might still
1629 * be NULL if the module contains no such data.
1630 *
1631 *@@added V0.9.7 (2000-12-18) [lafaix]
1632 *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
1633 *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
1634 *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
1635 */
1636
1637APIRET exehQueryResources(PEXECUTABLE pExec, // in: executable from exehOpen
1638 PFSYSRESOURCE *ppaResources, // out: res's array
1639 PULONG pcResources) // out: array item count
1640{
1641 if ( (pExec)
1642 && ( (pExec->ulOS == EXEOS_OS2)
1643 || (pExec->ulOS == EXEOS_WIN16)
1644 || (pExec->ulOS == EXEOS_WIN386)
1645 )
1646 )
1647 {
1648 ENSURE_BEGIN;
1649 ULONG cResources = 0;
1650 PFSYSRESOURCE paResources = NULL;
1651
1652 HFILE hfExe = pExec->pFile->hf;
1653 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
1654
1655 if (pExec->pDosExeHeader)
1656 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
1657 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
1658
1659 if (pExec->ulExeFormat == EXEFORMAT_LX)
1660 {
1661 // 32-bit OS/2 executable:
1662 PLXHEADER pLXHeader = pExec->pLXHeader;
1663 if (cResources = pLXHeader->ulResTblCnt)
1664 {
1665 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
1666 struct rsrc32 // Resource Table Entry
1667 {
1668 unsigned short type; // Resource type
1669 unsigned short name; // Resource name
1670 unsigned long cb; // Resource size
1671 unsigned short obj; // Object number
1672 unsigned long offset; // Offset within object
1673 } rs;
1674
1675 struct o32_obj // Flat .EXE object table entry
1676 {
1677 unsigned long o32_size; // Object virtual size
1678 unsigned long o32_base; // Object base virtual address
1679 unsigned long o32_flags; // Attribute flags
1680 unsigned long o32_pagemap; // Object page map index
1681 unsigned long o32_mapsize; // Number of entries in object page map
1682 unsigned long o32_reserved; // Reserved
1683 } ot;
1684 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
1685
1686 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
1687 ULONG ulDummy;
1688 int i;
1689 ULONG ulCurOfs;
1690
1691 paResources = (PFSYSRESOURCE)malloc(cb);
1692 if (!paResources)
1693 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1694
1695 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1696
1697 ENSURE_SAFE(DosSetFilePtr(hfExe,
1698 pLXHeader->ulResTblOfs
1699 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1700 FILE_BEGIN,
1701 &ulDummy));
1702
1703 for (i = 0; i < cResources; i++)
1704 {
1705 ENSURE_SAFE(DosRead(hfExe, &rs, 14, &ulDummy));
1706
1707 paResources[i].ulID = rs.name;
1708 paResources[i].ulType = rs.type;
1709 paResources[i].ulSize = rs.cb;
1710 paResources[i].ulFlag = rs.obj; // Temp storage for Object
1711 // number. Will be filled
1712 // with resource flag
1713 // later.
1714 }
1715
1716 for (i = 0; i < cResources; i++)
1717 {
1718 ULONG ulOfsThis = pLXHeader->ulObjTblOfs
1719 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1720 + ( sizeof(ot)
1721 * (paResources[i].ulFlag - 1));
1722
1723 ENSURE_SAFE(DosSetFilePtr(hfExe,
1724 ulOfsThis,
1725 FILE_BEGIN,
1726 &ulDummy));
1727
1728 ENSURE_SAFE(DosRead(hfExe, &ot, sizeof(ot), &ulDummy));
1729
1730 paResources[i].ulFlag = ((ot.o32_flags & OBJWRITE)
1731 ? 0
1732 : RNPURE);
1733 paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
1734 ? 4096
1735 : 0);
1736 paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
1737 ? RNMOVE
1738 : 0);
1739 paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
1740 ? RNPRELOAD
1741 : 0);
1742 } // end for
1743 } // end if (cResources)
1744 } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
1745 else if (pExec->ulExeFormat == EXEFORMAT_NE)
1746 {
1747 PNEHEADER pNEHeader = pExec->pNEHeader;
1748
1749 if (pExec->ulOS == EXEOS_OS2)
1750 {
1751 // 16-bit OS/2 executable:
1752 cResources = pNEHeader->usResSegmCount;
1753
1754 if (cResources)
1755 {
1756 #pragma pack(1) // V0.9.9 (2001-04-02) [umoeller]
1757 struct {unsigned short type; unsigned short name;} rti;
1758 struct new_seg // New .EXE segment table entry
1759 {
1760 unsigned short ns_sector; // File sector of start of segment
1761 unsigned short ns_cbseg; // Number of bytes in file
1762 unsigned short ns_flags; // Attribute flags
1763 unsigned short ns_minalloc; // Minimum allocation in bytes
1764 } ns;
1765 #pragma pack()
1766
1767 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
1768 ULONG ulDummy;
1769 int i;
1770
1771 paResources = (PFSYSRESOURCE)malloc(cb);
1772 if (!paResources)
1773 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1774
1775 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
1776
1777 // we first read the resources IDs and types
1778
1779 ENSURE_SAFE(DosSetFilePtr(hfExe,
1780 pNEHeader->usResTblOfs
1781 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1782 FILE_BEGIN,
1783 &ulDummy));
1784
1785 for (i = 0; i < cResources; i++)
1786 {
1787 ENSURE_SAFE(DosRead(hfExe, &rti, sizeof(rti), &ulDummy));
1788
1789 paResources[i].ulID = rti.name;
1790 paResources[i].ulType = rti.type;
1791 }
1792
1793 // we then read their sizes and flags
1794
1795 for (i = 0; i < cResources; i++)
1796 {
1797 ENSURE_SAFE(DosSetFilePtr(hfExe,
1798 ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
1799 + pNEHeader->usSegTblOfs
1800 + (sizeof(ns)
1801 * ( pNEHeader->usSegTblEntries
1802 - pNEHeader->usResSegmCount
1803 + i)),
1804 FILE_BEGIN,
1805 &ulDummy));
1806
1807 ENSURE_SAFE(DosRead(hfExe, &ns, sizeof(ns), &ulDummy));
1808
1809 paResources[i].ulSize = ns.ns_cbseg;
1810
1811 paResources[i].ulFlag = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
1812 paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
1813 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
1814 paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
1815 }
1816 } // end if (cResources)
1817 }
1818 else
1819 {
1820 // 16-bit Windows executable
1821 USHORT usAlignShift;
1822 ULONG ulDummy;
1823
1824 ENSURE(DosSetFilePtr(hfExe,
1825 pNEHeader->usResTblOfs
1826 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
1827 FILE_BEGIN,
1828 &ulDummy));
1829
1830 ENSURE(DosRead(hfExe,
1831 &usAlignShift,
1832 sizeof(usAlignShift),
1833 &ulDummy));
1834
1835 while (TRUE)
1836 {
1837 USHORT usTypeID;
1838 USHORT usCount;
1839
1840 ENSURE(DosRead(hfExe,
1841 &usTypeID,
1842 sizeof(usTypeID),
1843 &ulDummy));
1844
1845 if (usTypeID == 0)
1846 break;
1847
1848 ENSURE(DosRead(hfExe,
1849 &usCount,
1850 sizeof(usCount),
1851 &ulDummy));
1852
1853 ENSURE(DosSetFilePtr(hfExe,
1854 sizeof(ULONG),
1855 FILE_CURRENT,
1856 &ulDummy));
1857
1858 cResources += usCount;
1859
1860 // first pass, skip NAMEINFO table
1861 ENSURE(DosSetFilePtr(hfExe,
1862 usCount*6*sizeof(USHORT),
1863 FILE_CURRENT,
1864 &ulDummy));
1865 }
1866
1867 if (cResources)
1868 {
1869 USHORT usCurrent = 0;
1870 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
1871
1872 paResources = (PFSYSRESOURCE)malloc(cb);
1873 if (!paResources)
1874 ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
1875
1876 memset(paResources, 0, cb);
1877
1878 ENSURE_SAFE(DosSetFilePtr(hfExe,
1879 pNEHeader->usResTblOfs
1880 + ulNewHeaderOfs,
1881 FILE_BEGIN,
1882 &ulDummy));
1883
1884 ENSURE_SAFE(DosRead(hfExe,
1885 &usAlignShift,
1886 sizeof(usAlignShift),
1887 &ulDummy));
1888
1889 while (TRUE)
1890 {
1891 USHORT usTypeID;
1892 USHORT usCount;
1893 int i;
1894
1895 ENSURE_SAFE(DosRead(hfExe,
1896 &usTypeID,
1897 sizeof(usTypeID),
1898 &ulDummy));
1899
1900 if (usTypeID == 0)
1901 break;
1902
1903 ENSURE_SAFE(DosRead(hfExe,
1904 &usCount,
1905 sizeof(usCount),
1906 &ulDummy));
1907
1908 ENSURE_SAFE(DosSetFilePtr(hfExe,
1909 sizeof(ULONG),
1910 FILE_CURRENT,
1911 &ulDummy));
1912
1913 // second pass, read NAMEINFO table
1914 for (i = 0; i < usCount; i++)
1915 {
1916 USHORT usLength,
1917 usFlags,
1918 usID;
1919
1920 ENSURE_SAFE(DosSetFilePtr(hfExe,
1921 sizeof(USHORT),
1922 FILE_CURRENT,
1923 &ulDummy));
1924
1925 ENSURE_SAFE(DosRead(hfExe,
1926 &usLength,
1927 sizeof(USHORT),
1928 &ulDummy));
1929 ENSURE_SAFE(DosRead(hfExe,
1930 &usFlags,
1931 sizeof(USHORT),
1932 &ulDummy));
1933 ENSURE_SAFE(DosRead(hfExe,
1934 &usID,
1935 sizeof(USHORT),
1936 &ulDummy));
1937
1938 ENSURE_SAFE(DosSetFilePtr(hfExe,
1939 2*sizeof(USHORT),
1940 FILE_CURRENT,
1941 &ulDummy));
1942
1943 // !!! strings ids and types not handled yet
1944 // !!! 15th bit is used to denotes strings
1945 // !!! offsets [lafaix]
1946 paResources[usCurrent].ulType = usTypeID ^ 0x8000;
1947 paResources[usCurrent].ulID = usID ^ 0x8000;
1948 paResources[usCurrent].ulSize = usLength << usAlignShift;
1949 paResources[usCurrent].ulFlag = usFlags & 0x70;
1950
1951 usCurrent++;
1952 }
1953 }
1954 }
1955 }
1956 } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
1957 else
1958 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1959
1960 *ppaResources = paResources;
1961 *pcResources = cResources;
1962
1963 ENSURE_FINALLY;
1964 // if we had an error above, clean up
1965 free(paResources);
1966 ENSURE_END;
1967 }
1968 else
1969 ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
1970
1971 ENSURE_OK;
1972}
1973
1974/*
1975 *@@ exehFreeResources:
1976 * frees resources allocated by exehQueryResources.
1977 *
1978 *@@added V0.9.7 (2000-12-18) [lafaix]
1979 */
1980
1981APIRET exehFreeResources(PFSYSRESOURCE paResources)
1982{
1983 free(paResources);
1984 return NO_ERROR;
1985}
1986
1987/*
1988 *@@ exehLoadLXMaps:
1989 * loads the three main LX maps into the given
1990 * EXECUTABLE structure.
1991 *
1992 * This loads:
1993 *
1994 * 1) the LX resource table;
1995 *
1996 * 2) the LX object table;
1997 *
1998 * 3) the LX object _page_ table (object map).
1999 *
2000 * Note that this is not automatically called
2001 * by exehOpen to save time, since the LX
2002 * maps are not needed for all the other exe
2003 * functions. However, this does get called
2004 * from exehLoadLXResource if needed.
2005 *
2006 * This returns:
2007 *
2008 * -- NO_ERROR: all three LX maps were loaded,
2009 * and pExec->fLXMapsLoaded was set to TRUE.
2010 *
2011 * -- ERROR_INVALID_PARAMETER
2012 *
2013 * -- ERROR_INVALID_EXE_SIGNATURE: pExec does
2014 * not specify an LX executable.
2015 *
2016 * -- ERROR_NO_DATA: at least one of the structs
2017 * does not exist.
2018 *
2019 * -- ERROR_NOT_ENOUGH_MEMORY
2020 *
2021 * plus the error codes of doshReadAt.
2022 *
2023 * Call exehFreeLXMaps to clean up explicitly, but
2024 * that func automatically gets called by exehClose.
2025 *
2026 *@@added V0.9.16 (2001-12-08) [umoeller]
2027 */
2028
2029APIRET exehLoadLXMaps(PEXECUTABLE pExec)
2030{
2031 APIRET arc;
2032
2033 PLXHEADER pLXHeader;
2034
2035 if (!pExec)
2036 arc = ERROR_INVALID_PARAMETER;
2037 else if (pExec->fLXMapsLoaded)
2038 // already loaded:
2039 arc = NO_ERROR;
2040 else if ( (pExec->ulExeFormat != EXEFORMAT_LX)
2041 || (!(pLXHeader = pExec->pLXHeader))
2042 )
2043 arc = ERROR_INVALID_EXE_SIGNATURE;
2044 else
2045 {
2046 PXFILE pFile = pExec->pFile;
2047 ULONG ulNewHeaderOfs = 0;
2048 ULONG cb;
2049
2050 if (pExec->pDosExeHeader)
2051 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2052 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2053
2054 // resource table
2055 if ( (!(arc = doshAllocArray(pLXHeader->ulResTblCnt,
2056 sizeof(RESOURCETABLEENTRY),
2057 (PBYTE*)&pExec->pRsTbl,
2058 &cb)))
2059 && (!(arc = doshReadAt(pFile,
2060 pLXHeader->ulResTblOfs
2061 + ulNewHeaderOfs,
2062 &cb,
2063 (PBYTE)pExec->pRsTbl,
2064 DRFL_FAILIFLESS)))
2065 )
2066 {
2067 // object table
2068 if ( (!(arc = doshAllocArray(pLXHeader->ulObjCount,
2069 sizeof(OBJECTTABLEENTRY),
2070 (PBYTE*)&pExec->pObjTbl,
2071 &cb)))
2072 && (!(arc = doshReadAt(pFile,
2073 pLXHeader->ulObjTblOfs
2074 + ulNewHeaderOfs,
2075 &cb,
2076 (PBYTE)pExec->pObjTbl,
2077 DRFL_FAILIFLESS)))
2078 )
2079 {
2080 // object page table
2081 if ( (!(arc = doshAllocArray(pLXHeader->ulPageCount,
2082 sizeof(OBJECTPAGETABLEENTRY),
2083 (PBYTE*)&pExec->pObjPageTbl,
2084 &cb)))
2085 && (!(arc = doshReadAt(pFile,
2086 pLXHeader->ulObjPageTblOfs
2087 + ulNewHeaderOfs,
2088 &cb,
2089 (PBYTE)pExec->pObjPageTbl,
2090 DRFL_FAILIFLESS)))
2091 )
2092 {
2093 }
2094 }
2095 }
2096
2097 if (!arc)
2098 pExec->fLXMapsLoaded = TRUE;
2099 else
2100 exehFreeLXMaps(pExec);
2101 }
2102
2103 return arc;
2104}
2105
2106/*
2107 *@@ exehFreeLXMaps:
2108 * frees data allocated by exehLoadLXMaps.
2109 * Gets called automatically by exehClose.
2110 *
2111 *@@added V0.9.16 (2001-12-08) [umoeller]
2112 */
2113
2114VOID exehFreeLXMaps(PEXECUTABLE pExec)
2115{
2116 FREE(pExec->pRsTbl);
2117 FREE(pExec->pObjTbl);
2118 FREE(pExec->pObjPageTbl);
2119 pExec->fLXMapsLoaded = FALSE;
2120}
2121
2122// page flags (OBJECTPAGETABLEENTRY.o32_pageflags)
2123#define VALID 0x0000 // Valid Physical Page in .EXE
2124#define ITERDATA 0x0001 // Iterated Data Page
2125#define INVALID 0x0002 // Invalid Page
2126#define ZEROED 0x0003 // Zero Filled Page
2127#define RANGE 0x0004 // Range of pages
2128#define ITERDATA2 0x0005 // Iterated Data Page Type II
2129
2130/*
2131 *@@ ExpandIterdata1:
2132 * expands a page compressed with the old exepack
2133 * method introduced with OS/2 2.0 (plain /EXEPACK).
2134 *
2135 * Returns either ERROR_BAD_FORMAT or NO_ERROR.
2136 *
2137 * (C) Knut Stange Osmundsen. Used with permission.
2138 *
2139 *@@added V0.9.16 (2001-12-08) [umoeller]
2140 */
2141
2142static APIRET ExpandIterdata1(char *pabTarget, // out: page data (pagesize as in lx spec)
2143 int cbTarget, // in: sizeof *pabTarget (pagesize as in lx spec)
2144 const char *pabSource, // in: compressed source data in EXEPACK:1 format
2145 int cbSource) // in: sizeof *pabSource
2146{
2147 PLXITER pIter = (PLXITER)pabSource;
2148 // store the pointer for boundary checking
2149 char *pabTargetOriginal = pabTarget;
2150
2151 // validate size of data
2152 if (cbSource >= cbTarget - 2)
2153 return ERROR_BAD_FORMAT;
2154
2155 // expand the page
2156 while ( (pIter->LX_nIter)
2157 && (cbSource > 0)
2158 )
2159 {
2160 // check if we're out of bound
2161 ULONG nIter = pIter->LX_nIter,
2162 nBytes = pIter->LX_nBytes;
2163
2164 if ( (pabTarget - pabTargetOriginal + nIter * nBytes > cbTarget)
2165 || (cbSource <= 0)
2166 )
2167 return ERROR_BAD_FORMAT;
2168
2169 if (nBytes == 1)
2170 {
2171 // one databyte
2172 memset(pabTarget, pIter->LX_Iterdata, nIter);
2173 pabTarget += nIter;
2174 cbSource -= 4 + 1;
2175 pIter++;
2176 }
2177 else
2178 {
2179 int i;
2180 for (i = nIter;
2181 i > 0;
2182 i--, pabTarget += nBytes)
2183 memcpy(pabTarget, &pIter->LX_Iterdata, nBytes);
2184 cbSource -= 4 + nBytes;
2185 pIter = (PLXITER)((char*)pIter + 4 + nBytes);
2186 }
2187 }
2188
2189 // zero remaining part of the page
2190 if (pabTarget - pabTargetOriginal < cbTarget)
2191 memset(pabTarget, 0, cbTarget - (pabTarget - pabTargetOriginal));
2192
2193 return NO_ERROR;
2194}
2195
2196/*
2197 *@@ memcpyw:
2198 * a special memcpy for expandPage2 which performs a
2199 * word based copy. The difference between this, memmove
2200 * and memcpy is that we'll allways read words.
2201 *
2202 * (C) Knut Stange Osmundsen. Used with permission.
2203 *
2204 *@@added V0.9.16 (2001-12-08) [umoeller]
2205 */
2206
2207static void memcpyw(char *pch1, const char *pch2, size_t cch)
2208{
2209 /*
2210 * Use memcpy if possible.
2211 */
2212 if ((pch2 > pch1 ? pch2 - pch1 : pch1 - pch2) >= 4)
2213 {
2214 memcpy(pch1, pch2, cch); /* BUGBUG! ASSUMES that memcpy move NO more than 4 bytes at the time! */
2215 return;
2216 }
2217
2218 /*
2219 * Difference is less than 3 bytes.
2220 */
2221 if (cch & 1)
2222 *pch1++ = *pch2++;
2223
2224 for (cch >>= 1;
2225 cch > 0;
2226 cch--, pch1 += 2, pch2 += 2)
2227 *(PUSHORT)pch1 = *(PUSHORT)pch2;
2228}
2229
2230/*
2231 *@@ memcpyb:
2232 * a special memcpy for expandPage2 which performs a memmove
2233 * operation. The difference between this and memmove is that
2234 * this one works.
2235 *
2236 * (C) Knut Stange Osmundsen. Used with permission.
2237 *
2238 *@@added V0.9.16 (2001-12-08) [umoeller]
2239 */
2240
2241static void memcpyb(char *pch1, const char *pch2, size_t cch)
2242{
2243 /*
2244 * Use memcpy if possible.
2245 */
2246 if ((pch2 > pch1 ? pch2 - pch1 : pch1 - pch2) >= 4)
2247 {
2248 memcpy(pch1, pch2, cch);
2249 return;
2250 }
2251
2252 /*
2253 * Difference is less than 3 bytes.
2254 */
2255 while(cch--)
2256 *pch1++ = *pch2++;
2257}
2258
2259/*
2260 *@@ ExpandIterdata2:
2261 * expands a page compressed with the new exepack
2262 * method introduced with OS/2 Warp 3.0 (/EXEPACK:2).
2263 *
2264 * Returns either ERROR_BAD_FORMAT or NO_ERROR.
2265 *
2266 * (C) Knut Stange Osmundsen. Used with permission.
2267 *
2268 * Note that we call special (slow) memcpy versions
2269 * here because the standard memcpy will fail on
2270 * certain bit combinations here for some unknown
2271 * reason.
2272 *
2273 *@@added V0.9.16 (2001-12-08) [umoeller]
2274 */
2275
2276static APIRET ExpandIterdata2(char *pachPage, // out: page data (pagesize as in lx spec)
2277 int cchPage, // in: sizeof *pachPage (pagesize as in lx spec)
2278 const char *pachSrcPage, // in: compressed source data in EXEPACK:1 format
2279 int cchSrcPage) // in: size of source buf
2280{
2281 char *pachDestPage = pachPage; /* Store the pointer for boundrary checking. */
2282
2283 while (cchSrcPage > 0)
2284 {
2285 /*
2286 * Bit 0 and 1 is the encoding type.
2287 */
2288
2289 char cSrc = *pachSrcPage;
2290
2291 switch (cSrc & 0x03)
2292 {
2293 /*
2294 *
2295 * 0 1 2 3 4 5 6 7
2296 * type | |
2297 * ----------------
2298 * cch <cch bytes of data>
2299 *
2300 * Bits 2-7 is, if not zero, the length of an uncompressed run
2301 * starting at the following byte.
2302 *
2303 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
2304 * type | | | | | |
2305 * ---------------- ---------------------- -----------------------
2306 * zero cch char to multiply
2307 *
2308 * If the bits are zero, the following two bytes describes a
2309 * 1 byte interation run. First byte is count, second is the byte to copy.
2310 * A count of zero is means end of data, and we simply stops. In that case
2311 * the rest of the data should be zero.
2312 */
2313
2314 case 0:
2315 {
2316 if (cSrc)
2317 {
2318 int cch = cSrc >> 2;
2319 if ( (cchPage >= cch)
2320 && (cchSrcPage >= cch + 1)
2321 )
2322 {
2323 memcpy(pachPage, pachSrcPage + 1, cch);
2324 pachPage += cch, cchPage -= cch;
2325 pachSrcPage += cch + 1, cchSrcPage -= cch + 1;
2326 break; // switch (cSrc & 0x03)
2327 }
2328 return ERROR_BAD_FORMAT;
2329 }
2330
2331 if (cchSrcPage >= 2)
2332 {
2333 int cch;
2334 if (cch = pachSrcPage[1])
2335 {
2336 if ( (cchSrcPage >= 3)
2337 && (cchPage >= cch)
2338 )
2339 {
2340 memset(pachPage, pachSrcPage[2], cch);
2341 pachPage += cch, cchPage -= cch;
2342 pachSrcPage += 3, cchSrcPage -= 3;
2343 break; // switch (cSrc & 0x03)
2344 }
2345 }
2346 else
2347 goto endloop;
2348 }
2349
2350 return ERROR_BAD_FORMAT;
2351 }
2352
2353
2354 /*
2355 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2356 * type | | | | | |
2357 * ---- ------- -------------------------
2358 * cch1 cch2 - 3 offset <cch1 bytes of data>
2359 *
2360 * Two bytes layed out as described above, followed by cch1 bytes of data to be copied.
2361 * The cch2(+3) and offset describes an amount of data to be copied from the expanded
2362 * data relative to the current position. The data copied as you would expect it to be.
2363 */
2364
2365 case 1:
2366 {
2367 if (cchSrcPage >= 2)
2368 {
2369 int off = *(PUSHORT)pachSrcPage >> 7;
2370 int cch1 = cSrc >> 2 & 3;
2371 int cch2 = (cSrc >> 4 & 7) + 3;
2372 pachSrcPage += 2, cchSrcPage -= 2;
2373 if ( (cchSrcPage >= cch1)
2374 && (cchPage >= cch1 + cch2)
2375 && (pachPage + cch1 - off >= pachDestPage)
2376 )
2377 {
2378 memcpy(pachPage, pachSrcPage, cch1);
2379 pachPage += cch1, cchPage -= cch1;
2380 pachSrcPage += cch1, cchSrcPage -= cch1;
2381 memcpyb(pachPage, pachPage - off, cch2); //memmove doesn't do a good job here for some stupid reason.
2382 pachPage += cch2, cchPage -= cch2;
2383 break; // switch (cSrc & 0x03)
2384 }
2385 }
2386 return ERROR_BAD_FORMAT;
2387 }
2388
2389 /*
2390 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
2391 * type | | | |
2392 * ---- ----------------------------------
2393 * cch-3 offset
2394 *
2395 * Two bytes layed out as described above.
2396 * The cch(+3) and offset describes an amount of data to be copied from the expanded
2397 * data relative to the current position.
2398 *
2399 * If offset == 1 the data is not copied as expected, but in the memcpyw manner.
2400 */
2401
2402 case 2:
2403 {
2404 if (cchSrcPage >= 2)
2405 {
2406 int off = *(PUSHORT)pachSrcPage >> 4;
2407 int cch = (cSrc >> 2 & 3) + 3;
2408 pachSrcPage += 2, cchSrcPage -= 2;
2409 if ( (cchPage >= cch)
2410 && (pachPage - off >= pachDestPage)
2411 )
2412 {
2413 memcpyw(pachPage, pachPage - off, cch);
2414 pachPage += cch, cchPage -= cch;
2415 break; // switch (cSrc & 0x03)
2416 }
2417 }
2418 return ERROR_BAD_FORMAT;
2419 }
2420
2421
2422 /*
2423 * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
2424 * type | | | | | |
2425 * ---------- ---------------- ----------------------------------
2426 * cch1 cch2 offset <cch1 bytes of data>
2427 *
2428 * Three bytes layed out as described above, followed by cch1 bytes of data to be copied.
2429 * The cch2 and offset describes an amount of data to be copied from the expanded
2430 * data relative to the current position.
2431 *
2432 * If offset == 1 the data is not copied as expected, but in the memcpyw manner.
2433 */
2434
2435 case 3:
2436 {
2437 if (cchSrcPage >= 3)
2438 {
2439 int cch1 = cSrc >> 2 & 0x000f;
2440 int cch2 = *(PUSHORT)pachSrcPage >> 6 & 0x003f;
2441 int off = *(PUSHORT)(pachSrcPage + 1) >> 4;
2442 pachSrcPage += 3, cchSrcPage -= 3;
2443 if ( (cchSrcPage >= cch1)
2444 && (cchPage >= cch1 + cch2)
2445 && (pachPage - off + cch1 >= pachDestPage)
2446 )
2447 {
2448 memcpy(pachPage, pachSrcPage, cch1);
2449 pachPage += cch1, cchPage -= cch1;
2450 pachSrcPage += cch1, cchSrcPage -= cch1;
2451 memcpyw(pachPage, pachPage - off, cch2);
2452 pachPage += cch2, cchPage -= cch2;
2453 break; // switch (cSrc & 0x03)
2454 }
2455 }
2456 return ERROR_BAD_FORMAT;
2457 }
2458 } // end switch (cSrc & 0x03)
2459 }
2460
2461endloop:;
2462
2463 /*
2464 * Zero the rest of the page.
2465 */
2466 if (cchPage > 0)
2467 memset(pachPage, 0, cchPage);
2468
2469 return 0;
2470}
2471
2472/*
2473 *@@ GetOfsFromPageTableIndex:
2474 * returns the offset from the executable start
2475 * for the given page table index. This checks
2476 * the given index and returns ERROR_INVALID_SEGMENT_NUMBER
2477 * if it is invalid (for safety).
2478 *
2479 * Otherwise this returns NO_ERROR and sets
2480 * *pulPageOfs, *pulFlags, and *pulSize to
2481 * the respective fields from the page table
2482 * entry automatically.
2483 *
2484 * Note that the caller must manually add the
2485 * page data offset for resources (or whatever
2486 * other offset from the LX header is applicable).
2487 *
2488 *@@added V0.9.16 (2001-12-08) [umoeller]
2489 */
2490
2491static APIRET GetOfsFromPageTableIndex(PEXECUTABLE pExec, // in: executable from exehOpen
2492 ULONG ulObjPageTblIndexThis, // in: object page table index to look for
2493 PULONG pulFlags, // out: page flags
2494 PULONG pulSize, // out: page size
2495 PULONG pulPageOfs) // out: page ofs (add pLXHeader->ulDataPagesOfs to this)
2496{
2497 OBJECTPAGETABLEENTRY *pObjPageTblEntry;
2498
2499 // watch out for out of range, or we'll trap
2500 if (ulObjPageTblIndexThis - 1 >= pExec->pLXHeader->ulPageCount)
2501 {
2502 // _Pmpf(("ulObjPageTblIndexThis %d is too large", ulObjPageTblIndexThis));
2503 return ERROR_INVALID_SEGMENT_NUMBER; // 180
2504 }
2505
2506 pObjPageTblEntry = &pExec->pObjPageTbl[ulObjPageTblIndexThis - 1];
2507
2508 // page offset: shift left by what was specified in LX header
2509 *pulPageOfs = pObjPageTblEntry->o32_pagedataoffset
2510 << pExec->pLXHeader->ulPageLeftShift;
2511 *pulFlags = pObjPageTblEntry->o32_pageflags;
2512 *pulSize = pObjPageTblEntry->o32_pagesize;
2513
2514 return NO_ERROR;
2515}
2516
2517/*
2518 *@@ exehReadLXPage:
2519 * loads and possibly unpacks one LX page.
2520 *
2521 * In order to reduce memory allocations, the
2522 * caller is responsible for allocating a temp
2523 * buffer, which must be passed in with
2524 * pabCompressed.
2525 *
2526 * Returns:
2527 *
2528 * -- NO_ERROR: pbData was filled with data,
2529 * which is pLXHeader->ulPageSize in size.
2530 *
2531 * -- ERROR_INVALID_SEGMENT_NUMBER: segment
2532 * number is out of range.
2533 *
2534 * -- ERROR_BAD_FORMAT: compressed page data
2535 * is screwed somehow, or page size is
2536 * too large.
2537 *
2538 * plus the error codes of doshReadAt.
2539 *
2540 *@@added V0.9.16 (2002-01-05) [umoeller]
2541 */
2542
2543APIRET exehReadLXPage(PEXECUTABLE pExec, // in: executable from exehOpen
2544 ULONG ulObjPageTblIndex, // in: page table index to read
2545 ULONG ulExeOffset, // in: for resources, pLXHeader->ulDataPagesOfs
2546 PBYTE pabCompressed, // in: ptr to temp buffer which must be
2547 // pLXHeader->ulPageSize + 4 bytes in size
2548 PBYTE pbData) // out: ptr to buffer which receives actual
2549 // uncompressed page data (pLXHeader->ulPageSize)
2550{
2551 APIRET arc;
2552 ULONG ulFlags,
2553 ulSize,
2554 ulOffset;
2555
2556 if (!(arc = GetOfsFromPageTableIndex(pExec,
2557 ulObjPageTblIndex,
2558 &ulFlags,
2559 &ulSize,
2560 &ulOffset)))
2561 {
2562 ULONG ulPageSize = pExec->pLXHeader->ulPageSize;
2563
2564 ulOffset += ulExeOffset;
2565
2566 /* _Pmpf((" reading pgtbl %d, ofs %d, type %s",
2567 ulObjPageTblIndex,
2568 ulOffset,
2569 (ulFlags == 0x0001) ? "ITERDATA"
2570 : (ulFlags == 0x0005) ? "ITERDATA2"
2571 : "uncompressed")); */
2572
2573 if (ulSize > ulPageSize)
2574 arc = ERROR_BAD_FORMAT;
2575 // go read the page data (might be compressed)
2576 else if (!(arc = doshReadAt(pExec->pFile,
2577 ulOffset,
2578 &ulSize,
2579 pabCompressed,
2580 0)))
2581 {
2582 // _Pmpf((" %d bytes read", ulSize));
2583
2584 // terminate buffer for decompress
2585 *(PULONG)(pabCompressed + ulSize) = 0;
2586
2587 switch (ulFlags)
2588 {
2589 case ITERDATA:
2590 // OS/2 2.x:
2591 arc = ExpandIterdata1(pbData,
2592 ulPageSize,
2593 pabCompressed,
2594 ulSize); // this page's size
2595 break;
2596
2597 case ITERDATA2:
2598 // Warp 3:
2599 arc = ExpandIterdata2(pbData,
2600 ulPageSize,
2601 pabCompressed,
2602 ulSize); // this page's size
2603 break;
2604
2605 case VALID:
2606 // uncompressed
2607 memcpy(pbData,
2608 pabCompressed,
2609 ulPageSize);
2610 break;
2611 }
2612 }
2613 }
2614
2615 return arc;
2616}
2617
2618/*
2619 *@@ exehLoadLXResource:
2620 * attempts to load the data of the resource
2621 * with the specified type and id from an LX
2622 * executable.
2623 *
2624 * If (idResource == 0), the first resource of
2625 * the specified type is loaded. Otherwise we
2626 * try to find the resource of the specified
2627 * type _and_ ID.
2628 *
2629 * If NO_ERROR is returned, *ppbResData receives
2630 * a new buffer with the raw resource data, and
2631 * *pcbResData receives the size of that buffer.
2632 * The caller must then free() that buffer.
2633 *
2634 * This code will properly unpack compressed
2635 * pages in the executable so the returned
2636 * data is always unpacked and can be used
2637 * directly.
2638 *
2639 * Otherwise this returns:
2640 *
2641 * -- ERROR_INVALID_EXE_SIGNATURE: pExec is not
2642 * LX format.
2643 *
2644 * -- ERROR_NO_DATA: resource not found.
2645 *
2646 * -- ERROR_NOT_ENOUGH_MEMORY
2647 *
2648 * plus the error codes from exehReadLXPage.
2649 *
2650 *@@added V0.9.16 (2001-12-08) [umoeller]
2651 *@@changed V0.9.16 (2002-01-05) [umoeller]: largely rewritten to handle non-first icons properly
2652 */
2653
2654APIRET exehLoadLXResource(PEXECUTABLE pExec, // in: executable from exehOpen
2655 ULONG ulType, // in: RT_* type (e.g. RT_POINTER)
2656 ULONG idResource, // in: resource ID or 0 for first
2657 PBYTE *ppbResData, // out: resource data (to be free()'d)
2658 PULONG pcbResData) // out: size of resource data (ptr can be NULL)
2659{
2660 APIRET arc = NO_ERROR;
2661 ULONG cResources = 0;
2662
2663 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
2664
2665 PLXHEADER pLXHeader;
2666
2667 *ppbResData = 0;
2668
2669 /* _Pmpf((__FUNCTION__ " %s: ulType = %d, idResource %d",
2670 pExec->pFile->pszFilename,
2671 ulType, idResource)); */
2672
2673 if (!(pLXHeader = pExec->pLXHeader))
2674 return (ERROR_INVALID_EXE_SIGNATURE);
2675
2676 if (pExec->pDosExeHeader)
2677 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2678 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2679
2680 if (!(cResources = pLXHeader->ulResTblCnt))
2681 // no resources at all:
2682 return (ERROR_NO_DATA);
2683
2684 if (!pExec->fLXMapsLoaded)
2685 arc = exehLoadLXMaps(pExec);
2686
2687 if (!arc)
2688 {
2689 // alright, we're in:
2690
2691 // run thru the resources
2692 PXFILE pFile = pExec->pFile;
2693 BOOL fPtrFound = FALSE;
2694
2695 ULONG i;
2696 for (i = 0;
2697 i < cResources;
2698 i++)
2699 {
2700 // ptr to resource table entry
2701 RESOURCETABLEENTRY *pRsEntry = &pExec->pRsTbl[i];
2702
2703 // check resource type and ID
2704 if ( (pRsEntry->type == ulType)
2705 && ( (idResource == 0)
2706 || (idResource == pRsEntry->name)
2707 )
2708 )
2709 {
2710 // hooray, found it: that was the easy part...
2711 // finding the actual resource data isn't that
2712 // trivial:
2713
2714 // first find the object that the resource
2715 // resides in, but check the bounds
2716 if (pRsEntry->obj - 1 >= pLXHeader->ulObjCount)
2717 {
2718 // _Pmpf(("pRsEntry->obj %d is too large", pRsEntry->obj));
2719 arc = ERROR_INVALID_SEGMENT_NUMBER; // 180
2720 }
2721 else
2722 {
2723 // get the object table entry from the index
2724 OBJECTTABLEENTRY *pObjTblEntry = &pExec->pObjTbl[pRsEntry->obj - 1];
2725
2726 // get the object page table index for the
2727 // first resource entry in this object; to
2728 // this index we will need to add something
2729 // which depends on pRsEntry->offset
2730 ULONG ulObjPageTblIndex = pObjTblEntry->o32_pagemap;
2731
2732 ULONG ulPageSize = pLXHeader->ulPageSize;
2733
2734 // if this resource has specified an
2735 // offset into the object, this offset
2736 // specifies the offset in the uncompressed
2737 // data of the whole object:
2738 // for example:
2739 // res 0 ofs 0 cb 9808
2740 // res 1 ofs 9808 cb 3344
2741 // res 2 ofs 13152 cb ...
2742 // and so on.
2743 // So what we need to do is read in the page
2744 // where the resource offset points into (which
2745 // might be somewhere in the middle):
2746 ULONG ulFirstPage = pRsEntry->offset
2747 / ulPageSize;
2748
2749 // get the offset of the resource data into
2750 // that first page:
2751 ULONG ulResOffsetInFirstPage = pRsEntry->offset % ulPageSize;
2752
2753 ULONG ulLastPage = (pRsEntry->offset + pRsEntry->cb - 1)
2754 / ulPageSize;
2755
2756 // and we need as many pages as the resource occupies:
2757 ULONG cPages = ulLastPage - ulFirstPage + 1;
2758
2759 ULONG cbAlloc = 0;
2760
2761 // now allocate temporary buffers
2762 PBYTE pabCompressed = NULL,
2763 pabUncompressed = NULL;
2764
2765 // 4096 bytes for each page that is read in
2766 // plus 4 extra bytes to terminate for decompression
2767 if (!(pabCompressed = (PBYTE)malloc(ulPageSize + 4)))
2768 arc = ERROR_NOT_ENOUGH_MEMORY;
2769 // 4096 * cPages for the data that is composed from that
2770 else if (!(arc = doshAllocArray(cPages,
2771 ulPageSize,
2772 &pabUncompressed,
2773 &cbAlloc)))
2774 {
2775 // current pointer into pabUncompressed
2776 PBYTE pbCurrent = pabUncompressed;
2777
2778 ULONG ul,
2779 ulPageThis;
2780
2781 /* _Pmpf((" found RT_POINTER %d, size %d, resofs %d",
2782 pRsEntry->name,
2783 pRsEntry->cb,
2784 pRsEntry->offset));
2785 _Pmpf((" ulFirstPage %d, ulResOffsetInFirstPage %d, cPages %d",
2786 ulFirstPage, ulResOffsetInFirstPage, cPages)); */
2787
2788 ulPageThis = ulObjPageTblIndex + ulFirstPage;
2789
2790 // now go for each page:
2791 for (ul = 0;
2792 (ul < cPages) && (!arc);
2793 ul++, ulPageThis++)
2794 {
2795 if (!(arc = exehReadLXPage(pExec,
2796 ulPageThis,
2797 pLXHeader->ulDataPagesOfs,
2798 pabCompressed,
2799 pbCurrent)))
2800 {
2801 // got the data:
2802 // advance target buffer pointer for
2803 // next page
2804 pbCurrent += ulPageSize;
2805
2806 // make sure we don't write too far away
2807 if (pbCurrent > pabUncompressed + cbAlloc)
2808 arc = ERROR_BAD_FORMAT;
2809 }
2810 } // end for
2811
2812 // ok, now we got all the pages that do contain
2813 // data for this resource:
2814
2815 if (!arc)
2816 {
2817 // allocate a new buffer for caller
2818 if (!(*ppbResData = (PBYTE)malloc(pRsEntry->cb)))
2819 arc = ERROR_NOT_ENOUGH_MEMORY;
2820 else
2821 {
2822 // copy into that buffer from the offset
2823 // into the first page and the data from
2824 // the subsequent pages too
2825 memcpy(*ppbResData,
2826 pabUncompressed + ulResOffsetInFirstPage,
2827 pRsEntry->cb);
2828
2829 if (pcbResData)
2830 *pcbResData = pRsEntry->cb;
2831 }
2832
2833 fPtrFound = TRUE;
2834 }
2835
2836 FREE(pabUncompressed);
2837 FREE(pabCompressed);
2838 }
2839 }
2840 }
2841
2842 if (fPtrFound || arc)
2843 break;
2844
2845 } // end for
2846
2847 if ((!fPtrFound) && (!arc))
2848 arc = ERROR_NO_DATA;
2849 }
2850
2851 // _Pmpf((__FUNCTION__ ": returning %d", arc));
2852
2853 return arc;
2854}
2855
2856/*
2857 *@@ exehLoadOS2NEMaps:
2858 * loads the the two main OS/2 NE maps into the
2859 * given EXECUTABLE structure.
2860 *
2861 * This loads:
2862 *
2863 * 1) the OS/2 NE resource table;
2864 *
2865 * 2) the OS/2 NE segment table.
2866 *
2867 * Note that this is not automatically called
2868 * by exehOpen to save time, since the NE
2869 * maps are not needed for all the other exe
2870 * functions. However, this does get called
2871 * from exehLoadOS2NEResource if needed.
2872 *
2873 * This returns:
2874 *
2875 * -- NO_ERROR: both maps were loaded,
2876 * and pExec->fOS2NEMapsLoaded was set to TRUE.
2877 *
2878 * -- ERROR_INVALID_PARAMETER
2879 *
2880 * -- ERROR_INVALID_EXE_SIGNATURE: pExec does
2881 * not specify an NE executable, or the OS
2882 * flag is != OS/2. This func does not work
2883 * for Win16 executables.
2884 *
2885 * -- ERROR_NO_DATA: at least one of the structs
2886 * does not exist.
2887 *
2888 * -- ERROR_NOT_ENOUGH_MEMORY
2889 *
2890 * plus the error codes of doshReadAt.
2891 *
2892 * Call exehFreeNEMaps to clean up explicitly, but
2893 * that func automatically gets called by exehClose.
2894 *
2895 *@@added V0.9.16 (2001-12-08) [umoeller]
2896 */
2897
2898APIRET exehLoadOS2NEMaps(PEXECUTABLE pExec)
2899{
2900 APIRET arc;
2901
2902 PNEHEADER pNEHeader;
2903
2904 if (!pExec)
2905 arc = ERROR_INVALID_PARAMETER;
2906 else if (pExec->fOS2NEMapsLoaded)
2907 // already loaded:
2908 arc = NO_ERROR;
2909 else if ( (pExec->ulExeFormat != EXEFORMAT_NE)
2910 || (pExec->ulOS != EXEOS_OS2)
2911 || (!(pNEHeader = pExec->pNEHeader))
2912 )
2913 arc = ERROR_INVALID_EXE_SIGNATURE;
2914 else
2915 {
2916 PXFILE pFile = pExec->pFile;
2917 ULONG ulNewHeaderOfs = 0;
2918 ULONG cb;
2919
2920 if (pExec->pDosExeHeader)
2921 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
2922 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
2923
2924 // resource table
2925 if ( (!(arc = doshAllocArray(pNEHeader->usResSegmCount,
2926 sizeof(OS2NERESTBLENTRY),
2927 (PBYTE*)&pExec->paOS2NEResTblEntry,
2928 &cb)))
2929 && (!(arc = doshReadAt(pFile,
2930 pNEHeader->usResTblOfs
2931 + ulNewHeaderOfs,
2932 &cb,
2933 (PBYTE)pExec->paOS2NEResTblEntry,
2934 DRFL_FAILIFLESS)))
2935 )
2936 {
2937 // resource segments
2938 if ( (!(arc = doshAllocArray(pNEHeader->usResSegmCount,
2939 sizeof(OS2NESEGMENT),
2940 (PBYTE*)&pExec->paOS2NESegments,
2941 &cb)))
2942 && (!(arc = doshReadAt(pFile,
2943 pNEHeader->usResTblOfs
2944 + ulNewHeaderOfs
2945 - cb, // pNEHeader->usResSegmCount * sizeof(struct new_seg)
2946 &cb,
2947 (PBYTE)pExec->paOS2NESegments,
2948 DRFL_FAILIFLESS)))
2949 )
2950 {
2951 }
2952 }
2953
2954 if (!arc)
2955 pExec->fOS2NEMapsLoaded = TRUE;
2956 else
2957 exehFreeNEMaps(pExec);
2958 }
2959
2960 return arc;
2961}
2962
2963/*
2964 *@@ exehFreeNEMaps:
2965 * frees data allocated by exehLoadOS2NEMaps.
2966 * Gets called automatically by exehClose.
2967 *
2968 *@@added V0.9.16 (2001-12-08) [umoeller]
2969 */
2970
2971VOID exehFreeNEMaps(PEXECUTABLE pExec)
2972{
2973 FREE(pExec->paOS2NEResTblEntry);
2974 FREE(pExec->paOS2NESegments);
2975 pExec->fOS2NEMapsLoaded = FALSE;
2976}
2977
2978/*
2979 *@@ exehLoadOS2NEResource:
2980 * attempts to load the data of the resource
2981 * with the specified type and id from an OS/2
2982 * NE executable.
2983 *
2984 * Note that NE executables with resources in
2985 * OS/2 format are very, very rare. The
2986 * only OS/2 NE executables with resources I
2987 * could find at this point were in an old 1.3
2988 * Toolkit, but with them, this code works.
2989 *
2990 * If (idResource == 0), the first resource of
2991 * the specified type is loaded. Otherwise we
2992 * try to find the resource of the specified
2993 * type _and_ ID.
2994 *
2995 * If NO_ERROR is returned, *ppbResData receives
2996 * a new buffer with the raw resource data, and
2997 * *pcbResData receives the size of that buffer.
2998 * The caller must then free() that buffer.
2999 *
3000 * Since NE doesn't support packing, the data is
3001 * unpacked always and can be used directly.
3002 *
3003 * Otherwise this returns:
3004 *
3005 * -- ERROR_INVALID_EXE_SIGNATURE: pExec is not
3006 * NE or not OS/2. This func does not work
3007 * for Win16 executables.
3008 *
3009 * -- ERROR_NO_DATA: resource not found.
3010 *
3011 * -- ERROR_BAD_FORMAT: cannot handle resource format.
3012 *
3013 *@@added V0.9.16 (2001-12-08) [umoeller]
3014 */
3015
3016APIRET exehLoadOS2NEResource(PEXECUTABLE pExec, // in: executable from exehOpen
3017 ULONG ulType, // in: RT_* type (e.g. RT_POINTER)
3018 ULONG idResource, // in: resource ID or 0 for first
3019 PBYTE *ppbResData, // out: resource data (to be free()'d)
3020 PULONG pcbResData) // out: size of resource data (ptr can be NULL)
3021{
3022 APIRET arc = NO_ERROR;
3023 ULONG cResources = 0;
3024
3025 ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
3026
3027 PNEHEADER pNEHeader;
3028
3029 if (!(pNEHeader = pExec->pNEHeader))
3030 return (ERROR_INVALID_EXE_SIGNATURE);
3031
3032 if (pExec->pDosExeHeader)
3033 // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
3034 ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
3035
3036 // _Pmpf((__FUNCTION__ ": entering, checking %d resources", pNEHeader->usResSegmCount));
3037
3038 if (!(cResources = pNEHeader->usResSegmCount))
3039 // no resources at all:
3040 return (ERROR_NO_DATA);
3041
3042 if (!pExec->fOS2NEMapsLoaded)
3043 arc = exehLoadOS2NEMaps(pExec);
3044
3045 if (!arc)
3046 {
3047 // alright, we're in:
3048 PXFILE pFile = pExec->pFile;
3049
3050 // run thru the resources
3051 BOOL fPtrFound = FALSE;
3052
3053 ULONG i;
3054 POS2NERESTBLENTRY pResTblEntryThis = pExec->paOS2NEResTblEntry;
3055 POS2NESEGMENT pSegThis = pExec->paOS2NESegments;
3056 for (i = 0;
3057 i < cResources;
3058 i++, pResTblEntryThis++, pSegThis++)
3059 {
3060 // check resource type and ID
3061 if ( (pResTblEntryThis->usType == ulType)
3062 && ( (idResource == 0)
3063 || (idResource == pResTblEntryThis->usID)
3064 )
3065 )
3066 {
3067 // hooray, we found the resource...
3068
3069 // look up the corresponding segment
3070
3071 ULONG ulOffset = ( (ULONG)pSegThis->ns_sector
3072 << pNEHeader->usLogicalSectShift
3073 );
3074
3075 ULONG cb = pSegThis->ns_cbseg; // resource size
3076 PBYTE pb;
3077 if (!(*ppbResData = (PBYTE)malloc(cb)))
3078 arc = ERROR_NOT_ENOUGH_MEMORY;
3079 else
3080 {
3081 if (!(arc = doshReadAt(pFile,
3082 ulOffset,
3083 &cb,
3084 *ppbResData,
3085 DRFL_FAILIFLESS)))
3086 {
3087 if (pcbResData)
3088 *pcbResData = cb;
3089 fPtrFound = TRUE;
3090 }
3091 else
3092 // error reading:
3093 free(*ppbResData);
3094 }
3095 }
3096
3097 if (fPtrFound || arc)
3098 break;
3099
3100 } // end for
3101
3102 if ((!fPtrFound) && (!arc))
3103 arc = ERROR_NO_DATA;
3104 }
3105 // else
3106 // _Pmpf(("exehLoadOS2NEMaps returned %d"));
3107
3108 return arc;
3109}
3110
3111/*
3112 *@@ exehClose:
3113 * this closes an executable opened with exehOpen.
3114 * Always call this function if NO_ERROR was returned by
3115 * exehOpen.
3116 *
3117 * This automaticall calls exehFreeLXMaps and
3118 * exehFreeNEMaps.
3119 *
3120 *@@added V0.9.0 [umoeller]
3121 *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed memory leaks
3122 *@@changed V0.9.16 (2001-12-08) [umoeller]: changed prototype to null the pExec ptr
3123 */
3124
3125APIRET exehClose(PEXECUTABLE *ppExec)
3126{
3127 APIRET arc = NO_ERROR;
3128 PEXECUTABLE pExec;
3129 if ( (ppExec)
3130 && (pExec = *ppExec)
3131 )
3132 {
3133 char **papsz[] =
3134 {
3135 (char**)&pExec->pDosExeHeader,
3136 (char**)&pExec->pNEHeader,
3137 (char**)&pExec->pLXHeader,
3138 (char**)&pExec->pPEHeader,
3139
3140 &pExec->pszDescription,
3141 &pExec->pszVendor,
3142 &pExec->pszVersion,
3143 &pExec->pszInfo,
3144
3145 &pExec->pszBuildDateTime,
3146 &pExec->pszBuildMachine,
3147 &pExec->pszASD,
3148 &pExec->pszLanguage,
3149 &pExec->pszCountry,
3150 &pExec->pszRevision,
3151 &pExec->pszUnknown,
3152 &pExec->pszFixpak
3153 };
3154 ULONG ul;
3155
3156 exehFreeLXMaps(pExec);
3157 exehFreeNEMaps(pExec);
3158
3159 // fixed the memory leaks with the missing fields,
3160 // turned this into a loop
3161 for (ul = 0;
3162 ul < sizeof(papsz) / sizeof(papsz[0]);
3163 ul++)
3164 {
3165 PSZ pThis;
3166 if (pThis = *papsz[ul])
3167 {
3168 free(pThis);
3169 *papsz[ul] = NULL;
3170 }
3171 }
3172
3173 doshClose(&pExec->pFile);
3174
3175 free(pExec);
3176 *ppExec = NULL;
3177 }
3178 else
3179 arc = ERROR_INVALID_PARAMETER;
3180
3181 return arc;
3182}
3183
Note: See TracBrowser for help on using the repository browser.