source: branches/branch-1-0/src/helpers/exeh.c@ 365

Last change on this file since 365 was 262, checked in by pr, 21 years ago

XWP bug 583

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