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

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

Misc changes.

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