Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

634 lines
20 KiB

  1. /*static char *SCCSID = "%W% %E%";*/
  2. /*
  3. * Copyright Microsoft Corporation 1983-1987
  4. *
  5. * This Module contains Proprietary Information of Microsoft
  6. * Corporation and should be treated as Confidential.
  7. */
  8. /* Exepack */
  9. /****************************************************************
  10. * *
  11. * PACK.C *
  12. * *
  13. ****************************************************************/
  14. #include <minlit.h> /* Types, constants */
  15. #include <bndtrn.h> /* More types and constants */
  16. #include <bndrel.h> /* More types and constants */
  17. #include <lnkio.h> /* Linker I/O definitions */
  18. #include <lnkmsg.h> /* Error messages */
  19. #include <extern.h> /* External declarations */
  20. #if FEXEPACK AND ODOS3EXE /* Whole file is conditional */
  21. typedef struct _RUNTYPE
  22. {
  23. WORD wSignature;
  24. WORD cbLastp;
  25. WORD cpnRes;
  26. WORD irleMax;
  27. WORD cparDirectory;
  28. WORD cparMinAlloc;
  29. WORD cparMaxAlloc;
  30. WORD saStack;
  31. WORD raStackInit;
  32. WORD wchksum;
  33. WORD raStart;
  34. WORD saStart;
  35. WORD rbrgrle;
  36. WORD iovMax;
  37. WORD doslev;
  38. }
  39. RUNTYPE;
  40. /* States of automaton */
  41. #define STARTSTATE 0
  42. #define FINDREPEAT 1
  43. #define FINDENDRPT 2
  44. #define EMITRECORD 3
  45. /*
  46. * LOCAL FUNCTION PROTOTYPES
  47. */
  48. LOCAL void NEAR EmitRecords(void);
  49. LOCAL unsigned char NEAR GetFromVM(void);
  50. LOCAL unsigned short NEAR ScanWhileSame(void);
  51. LOCAL unsigned short NEAR ScanWhileDifferent(void);
  52. LOCAL WORD NEAR AfterScanning(unsigned short l);
  53. LOCAL void NEAR OutEnum(void);
  54. LOCAL void NEAR OutIter(SATYPE sa, WORD length);
  55. /*
  56. * DATA DEFINED IN UNPACK MODULE: unpack.asm
  57. */
  58. #if NOT defined( _WIN32 )
  59. extern char * FAR cdecl UnpackModule; /* Unpacker/Relocator module */
  60. extern char FAR cdecl SegStart; // Start of unpacker
  61. extern WORD FAR cdecl cbUnpack; /* Length of UnpackModule */
  62. extern WORD FAR cdecl ipsave; /* Original IP */
  63. extern WORD FAR cdecl cssave; /* Original CS */
  64. extern WORD FAR cdecl spsave; /* Original SP */
  65. extern WORD FAR cdecl sssave; /* Original SS */
  66. extern WORD FAR cdecl cparExp; /* # para. in expanded image */
  67. extern WORD FAR cdecl raStartUnpack; /* Offset of code start in unpacker */
  68. extern WORD FAR cdecl raMoveUnpack; /* Offset of self-moving in unpacker */
  69. extern WORD FAR cdecl raBadStack; /* Bottom of bad stack range */
  70. extern WORD FAR cdecl szBadStack; /* Bad stack range */
  71. #else // _WIN32
  72. //
  73. // For the portable NTGroup version of this linker, we can't use unpack32.asm
  74. // directly because we need to run on RISC platforms. But we still need this
  75. // code, which is real-mode x86 code tacked onto the DOS binary which unpacks
  76. // the packed EXE and then calls the real entrypoint. So it's defined as a
  77. // byte array, and the interesting offsets are hard-coded here. I came across
  78. // these values and the code debugging link3216.exe built as the languages group
  79. // did.
  80. //
  81. #define SegStart unpack
  82. #define cbUnpack (*(WORD *) &unpack[6])
  83. #define ipsave (*(WORD *) &unpack[0])
  84. #define cssave (*(WORD *) &unpack[2])
  85. #define spsave (*(WORD *) &unpack[8])
  86. #define sssave (*(WORD *) &unpack[0xa])
  87. #define cparExp (*(WORD *) &unpack[0xc])
  88. #define raStartUnpack 0x10
  89. #define raMoveUnpack 0x33
  90. #define raBadStack 0
  91. #define szBadStack 0x35
  92. unsigned char unpack[] = {
  93. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x01,
  94. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x42,
  95. 0x8b, 0xe8, 0x8c, 0xc0, 0x05, 0x10, 0x00, 0x0e,
  96. 0x1f, 0xa3, 0x04, 0x00, 0x03, 0x06, 0x0c, 0x00,
  97. 0x8e, 0xc0, 0x8b, 0x0e, 0x06, 0x00, 0x8b, 0xf9,
  98. 0x4f, 0x8b, 0xf7, 0xfd, 0xf3, 0xa4, 0x50, 0xb8,
  99. 0x34, 0x00, 0x50, 0xcb, 0x8c, 0xc3, 0x8c, 0xd8,
  100. 0x48, 0x8e, 0xd8, 0x8e, 0xc0, 0xbf, 0x0f, 0x00,
  101. 0xb9, 0x10, 0x00, 0xb0, 0xff, 0xf3, 0xae, 0x47,
  102. 0x8b, 0xf7, 0x8b, 0xc3, 0x48, 0x8e, 0xc0, 0xbf,
  103. 0x0f, 0x00, 0xb1, 0x04, 0x8b, 0xc6, 0xf7, 0xd0,
  104. 0xd3, 0xe8, 0x8c, 0xda, 0x2b, 0xd0, 0x73, 0x04,
  105. 0x8c, 0xd8, 0x2b, 0xd2, 0xd3, 0xe0, 0x03, 0xf0,
  106. 0x8e, 0xda, 0x8b, 0xc7, 0xf7, 0xd0, 0xd3, 0xe8,
  107. 0x8c, 0xc2, 0x2b, 0xd0, 0x73, 0x04, 0x8c, 0xc0,
  108. 0x2b, 0xd2, 0xd3, 0xe0, 0x03, 0xf8, 0x8e, 0xc2,
  109. 0xac, 0x8a, 0xd0, 0x4e, 0xad, 0x8b, 0xc8, 0x46,
  110. 0x8a, 0xc2, 0x24, 0xfe, 0x3c, 0xb0, 0x75, 0x05,
  111. 0xac, 0xf3, 0xaa, 0xeb, 0x06, 0x3c, 0xb2, 0x75,
  112. 0x6d, 0xf3, 0xa4, 0x8a, 0xc2, 0xa8, 0x01, 0x74,
  113. 0xb1, 0xbe, 0x32, 0x01, 0x0e, 0x1f, 0x8b, 0x1e,
  114. 0x04, 0x00, 0xfc, 0x33, 0xd2, 0xad, 0x8b, 0xc8,
  115. 0xe3, 0x13, 0x8b, 0xc2, 0x03, 0xc3, 0x8e, 0xc0,
  116. 0xad, 0x8b, 0xf8, 0x83, 0xff, 0xff, 0x74, 0x11,
  117. 0x26, 0x01, 0x1d, 0xe2, 0xf3, 0x81, 0xfa, 0x00,
  118. 0xf0, 0x74, 0x16, 0x81, 0xc2, 0x00, 0x10, 0xeb,
  119. 0xdc, 0x8c, 0xc0, 0x40, 0x8e, 0xc0, 0x83, 0xef,
  120. 0x10, 0x26, 0x01, 0x1d, 0x48, 0x8e, 0xc0, 0xeb,
  121. 0xe2, 0x8b, 0xc3, 0x8b, 0x3e, 0x08, 0x00, 0x8b,
  122. 0x36, 0x0a, 0x00, 0x03, 0xf0, 0x01, 0x06, 0x02,
  123. 0x00, 0x2d, 0x10, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
  124. 0xbb, 0x00, 0x00, 0xfa, 0x8e, 0xd6, 0x8b, 0xe7,
  125. 0xfb, 0x8b, 0xc5, 0x2e, 0xff, 0x2f, 0xb4, 0x40,
  126. 0xbb, 0x02, 0x00, 0xb9, 0x16, 0x00, 0x8c, 0xca,
  127. 0x8e, 0xda, 0xba, 0x1c, 0x01, 0xcd, 0x21, 0xb8,
  128. 0xff, 0x4c, 0xcd, 0x21, 0x50, 0x61, 0x63, 0x6b,
  129. 0x65, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
  130. 0x69, 0x73, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x75,
  131. 0x70, 0x74
  132. };
  133. #endif // _WIN32
  134. LOCAL WORD lastc; /* last character */
  135. LOCAL WORD c; /* current or next character */
  136. LOCAL WORD State = STARTSTATE;
  137. /* current state */
  138. LOCAL FTYPE fEnumOK; /* OK to emit enumerated records */
  139. LOCAL WORD cbRepeat; /* length of repeated stream */
  140. LOCAL WORD cbEnum; /* length of enumerated stream */
  141. #define EHLEN 3 /* 2 for length + 1 for type */
  142. #define MAXRPT 0xfff0 /* Maximum length to compress */
  143. #define MAXENM (0xfff0-(EHLEN+1))
  144. /* Maximum length of enum. stream */
  145. #define MINEXP (2*EHLEN+2) /* Minimum length of repeated stream,
  146. * after the first repeate record */
  147. #define toAdr20(seg, off) (((long)seg << 4) + off)
  148. LOCAL WORD minRpt = (18 * EHLEN) + 1;
  149. /* Minimum for rpt rec begins larger */
  150. /* Type values for expansion record headers */
  151. #define RPTREC 0xb0 /* Repeat record */
  152. #define ENMREC 0xb2 /* Enumerated record */
  153. /*
  154. * OutPack - Run a buffer through the compactor. Return value is
  155. * undefined.
  156. */
  157. void OutPack (pb, cb)
  158. REGISTER BYTE *pb; /* Pointer to buffer */
  159. unsigned cb; /* Number of bytes to compress */
  160. {
  161. REGISTER BYTE *endp; /* Pointer to end of buffer */
  162. endp = &pb[cb];
  163. while (pb < endp)
  164. switch (State)
  165. {
  166. case STARTSTATE:
  167. lastc = *pb++;
  168. State = FINDREPEAT;
  169. break;
  170. case FINDREPEAT:
  171. if (cbEnum >= MAXENM)
  172. {
  173. EmitRecords();
  174. State = FINDREPEAT;
  175. break;
  176. }
  177. c = *pb++;
  178. if (c == lastc)
  179. {
  180. cbRepeat = 2;
  181. State = FINDENDRPT;
  182. break;
  183. }
  184. /* At this point c != lastc */
  185. fputc(lastc, bsRunfile);
  186. cbEnum++;
  187. lastc = c;
  188. break;
  189. case FINDENDRPT:
  190. c = *pb++;
  191. if (c == lastc && cbRepeat < MAXRPT)
  192. {
  193. cbRepeat++;
  194. break;
  195. }
  196. if (cbRepeat < minRpt)
  197. {
  198. /*
  199. * Not long enough. Enum record swallows
  200. * repeated chars.
  201. */
  202. while (cbEnum <= MAXENM && cbRepeat > 0)
  203. {
  204. fputc(lastc, bsRunfile);
  205. cbEnum++;
  206. cbRepeat--;
  207. }
  208. if (cbRepeat > 0)
  209. EmitRecords();
  210. } else
  211. EmitRecords();
  212. lastc = c; /* Prepare for next stream */
  213. State = FINDREPEAT;
  214. }
  215. }
  216. /*
  217. * EmitRecords - Emits 1 or 2 expansion records. Return value is
  218. * undefined.
  219. */
  220. LOCAL void NEAR EmitRecords ()
  221. {
  222. /* We have 1 or 2 records to output */
  223. if (cbEnum > 0)
  224. {
  225. #if MDEBUG AND FDEBUG
  226. if (fDebug) fprintf(stdout, "E%8x\n", cbEnum);
  227. #endif
  228. if (fEnumOK)
  229. {
  230. /* Output an enumerated record header */
  231. OutWord(cbEnum);
  232. fputc(ENMREC, bsRunfile);
  233. }
  234. cbEnum = 0;
  235. }
  236. if (cbRepeat >= minRpt)
  237. {
  238. #if MDEBUG AND FDEBUG
  239. if (fDebug) fprintf(stdout, "R%8x\n", cbRepeat);
  240. #endif
  241. /* Output a repeat record */
  242. fputc(lastc, bsRunfile);
  243. OutWord(cbRepeat);
  244. if (!fEnumOK)
  245. {
  246. /* 1st record header generated */
  247. fputc(RPTREC|1, bsRunfile);
  248. fEnumOK = 1;
  249. } else
  250. fputc(RPTREC, bsRunfile);
  251. cbRepeat = 0;
  252. minRpt = MINEXP; /* 1st is out, so reset minRpt */
  253. } else if (cbRepeat > 0)
  254. {
  255. cbEnum = cbRepeat;
  256. while (cbRepeat-- > 0)
  257. fputc(lastc, bsRunfile);
  258. }
  259. }
  260. /*
  261. * EndPack - End the packing procedure: add the relocator module.
  262. */
  263. void EndPack (prun)
  264. RUNTYPE *prun; /* Pointer to runfile header */
  265. {
  266. long fpos; /* File position */
  267. WORD cparPacked; /* # paras in packed image */
  268. WORD cparUnpack; /* Size of Unpack module (paras) */
  269. int i;
  270. int crle; /* Count of relocs for a frame */
  271. long cTmp; /* Temporary count */
  272. long us; /* User's stack in minalloc */
  273. long los; /* Low end of forbidden stack */
  274. fseek(bsRunfile, 0L, 2); /* Go to end of file */
  275. cTmp = (((((long)prun->cpnRes-1)<<5) - prun->cparDirectory) << 4) +
  276. (prun->cbLastp ? prun->cbLastp : 512);
  277. /* Get # bytes in expanded image */
  278. cbRepeat += (WORD) (0xf & (0x10 - (0xf & cTmp)));
  279. /* Make it look like image ends on
  280. * paragraph boundary */
  281. if (State == FINDREPEAT)
  282. {
  283. fputc(lastc, bsRunfile); /* Update last enum record */
  284. cbEnum++;
  285. }
  286. minRpt = 1; /* Force final repeat rec. out */
  287. EmitRecords(); /* Output the final record(s) */
  288. cparExp = (short) ((cTmp + 0xf) >> 4);/* Save # paras in image */
  289. /*
  290. * Append the unpacking module (relocator-expander)
  291. */
  292. fpos = ftell(bsRunfile); /* Save where Unpack begins */
  293. /* Align Unpack on paragraph boundary */
  294. while (fpos & 0x0f)
  295. {
  296. fpos++;
  297. fputc(0xff, bsRunfile);
  298. }
  299. /* Make sure User stack won't stomp on unpack code */
  300. us = toAdr20(prun->saStack, prun->raStackInit);
  301. los = toAdr20((cTmp >> 4), raBadStack);
  302. while ( us > los && us - los < szBadStack )
  303. {
  304. for (i = 0; i < 16; i++)
  305. {
  306. us--;
  307. fpos++;
  308. fputc(0xff, bsRunfile);
  309. }
  310. }
  311. fflush(bsRunfile);
  312. cparPacked = (WORD) ((fpos >> 4) - prun->cparDirectory);
  313. if (cTmp < ((long)cparPacked << 4) + raMoveUnpack)
  314. Fatal(ER_badpack);
  315. /* Append relocator module (Unpack). This code depends
  316. * closely on the structure of unpack.asm. */
  317. /* Get length of relocation area */
  318. for (crle = 0, i = 0; i < 16; i++)
  319. crle += (mpframeRlc[i].count + 1) << 1;
  320. /* Initialize stack of relocator module */
  321. ipsave = prun->raStart;
  322. cssave = prun->saStart;
  323. #if defined( _WIN32 )
  324. if (cbUnpack != sizeof(unpack))
  325. Fatal(ER_badpack);
  326. #endif
  327. i = cbUnpack;
  328. cbUnpack += (WORD)crle;
  329. spsave = prun->raStackInit;
  330. sssave = prun->saStack;
  331. #ifdef M_BYTESWAP
  332. bswap(Unpack, 7);
  333. #endif
  334. #if DOSX32
  335. WriteExe(&SegStart, i);
  336. #else
  337. fwrite(UnpackModule, 1, i, bsRunfile);
  338. #endif
  339. /* Append optimized relocation records */
  340. for (i = 0; i < 16; i++)
  341. {
  342. crle = mpframeRlc[i].count;
  343. OutWord((WORD) crle);
  344. WriteExe(mpframeRlc[i].rgRlc, crle * sizeof(WORD));
  345. }
  346. /* Correct header values */
  347. fpos += cbUnpack;
  348. prun->cbLastp = (WORD) (fpos & 0x1ff);
  349. prun->cpnRes = (WORD) ((fpos + 511) >> 9);
  350. prun->irleMax = 0;
  351. cparUnpack = (cbUnpack + 0xf) >> 4;
  352. prun->cparMinAlloc = (cparExp + max(prun->cparMinAlloc,(cparUnpack+8)))
  353. - (cparPacked + cparUnpack);
  354. if (prun->cparMaxAlloc < prun->cparMinAlloc)
  355. prun->cparMaxAlloc = prun->cparMinAlloc;
  356. prun->saStack = cparExp + cparUnpack;
  357. prun->raStackInit = 128;
  358. prun->raStart = raStartUnpack;
  359. prun->saStart = cparPacked;
  360. fseek(bsRunfile, 0L, 0);
  361. OutHeader((struct exe_hdr *) prun);
  362. fseek(bsRunfile, fpos, 0);
  363. }
  364. #ifdef M_BYTESWAP
  365. /*
  366. * Swap bytes for 1st n words in buffer.
  367. */
  368. LOCAL bswap (buf, n)
  369. REGISTER char *buf;
  370. REGISTER int n;
  371. {
  372. REGISTER char swapb;
  373. for ( ; n-- > 0 ; buf += 2)
  374. {
  375. swapb = buf[0];
  376. buf[0] = buf[1];
  377. buf[1] = swapb;
  378. }
  379. }
  380. #endif
  381. #endif /*FEXEPACK AND ODOS3EXE*/
  382. /*
  383. * The following routines concern packing segmented-executable format
  384. * files.
  385. */
  386. #if FALSE
  387. #define MINREPEAT 32 /* Min length of iteration cousing compression */
  388. LOCAL long vaLast; /* Virtual address */
  389. LOCAL long vaStart; /* Virtual scanning start address */
  390. LOCAL long BufEnd; /* Virtual address of buffer end */
  391. #if EXE386
  392. LOCAL long ra; /* Offset within packed segment */
  393. #endif
  394. LOCAL BYTE LastB;
  395. LOCAL BYTE CurrentB;
  396. LOCAL long VPageAddress; /* Virtual address of current page */
  397. LOCAL WORD VPageOffset; /* Current position within virtual page */
  398. LOCAL BYTE *PageBuffer; /* Virtual page buffer */
  399. LOCAL BYTE NEAR GetFromVM()
  400. {
  401. if (VPageOffset == PAGLEN)
  402. {
  403. PageBuffer = mapva(VPageAddress, FALSE);
  404. /* Fetch page */
  405. VPageAddress += PAGLEN; /* Update page virtual address */
  406. VPageOffset = 0; /* Init page offset */
  407. }
  408. return(PageBuffer[VPageOffset++]);
  409. }
  410. LOCAL WORD NEAR ScanWhileSame()
  411. {
  412. long l;
  413. l = 2L; /* We are looking at two bytes in buffer */
  414. while (CurrentB == LastB)
  415. {
  416. if (vaStart + l >= BufEnd)
  417. return((WORD) l); /* We bump the buffer end */
  418. CurrentB = GetFromVM();
  419. l++;
  420. }
  421. return(l == 2L ? 0 : (WORD) (l - 1));
  422. /* We went one byte too far to detect they are different */
  423. }
  424. LOCAL WORD NEAR ScanWhileDifferent()
  425. {
  426. long l;
  427. l = 2L; /* We are looking at two bytes in buffer */
  428. while (CurrentB != LastB)
  429. {
  430. if (vaStart + l >= BufEnd)
  431. return((WORD) l); /* We bump the buffer end */
  432. LastB = CurrentB;
  433. CurrentB = GetFromVM();
  434. l++;
  435. }
  436. return((WORD) (l - 2)); /* We went two bytes too far to detect they are the same */
  437. }
  438. LOCAL WORD NEAR AfterScanning(l)
  439. WORD l; /* Length of scanned bytes */
  440. {
  441. vaStart += l; /* Update scan start address */
  442. #if EXE386
  443. ra += l; /* Update offset in segment */
  444. #endif
  445. if (vaStart + 2 >= BufEnd)
  446. { /* We need at least to bytes remaining */
  447. return(FALSE); /* Buffer end */
  448. }
  449. else
  450. {
  451. if (LastB != CurrentB)
  452. { /* We stop at iterated and enumerated */
  453. LastB = CurrentB; /* byte sequence, so we have move */
  454. CurrentB = GetFromVM(); /* one byte forward */
  455. }
  456. return((WORD) TRUE);
  457. }
  458. }
  459. LOCAL void NEAR OutEnum(void)
  460. {
  461. #if EXE386
  462. if (ExeFormat == Exe386)
  463. OutVm(vaLast, vaStart - vaLast);
  464. else
  465. {
  466. #endif
  467. OutWord(1);
  468. OutWord((WORD) (vaStart - vaLast));
  469. OutVm(vaLast, vaStart - vaLast);
  470. #if EXE386
  471. }
  472. #endif
  473. PageBuffer = mapva((VPageAddress - PAGLEN), FALSE);
  474. /* Refetch page */
  475. }
  476. LOCAL void NEAR OutIter(SATYPE sa, WORD length)
  477. {
  478. #if EXE386
  479. ITER idata; /* Iterated data description for range */
  480. if (ExeFormat == Exe386)
  481. {
  482. idata.iterations = (DWORD) length;
  483. idata.length = (DWORD) 1;
  484. idata.data = (DWORD) LastB;
  485. UpdateRanges(ShortIterData, sa, ra, &idata);
  486. }
  487. else
  488. {
  489. #endif
  490. OutWord(length);
  491. OutWord(1);
  492. OutByte(bsRunfile, LastB);
  493. #if EXE386
  494. }
  495. #endif
  496. }
  497. /*
  498. * Out5Pack - Run a buffer through the compactor. Return value is
  499. * starting position where data was written to output file.
  500. */
  501. long Out5Pack (sa, packed)
  502. SATYPE sa; /* File segment to be packed */
  503. WORD *packed; /* TRUE if iterated records written */
  504. {
  505. WORD proceed; /* True if there are bytes to scan */
  506. WORD length; /* Scanned bytes length */
  507. long lfaStart; /* Starting file address */
  508. lfaStart = ftell(bsRunfile); /* Get the starting address */
  509. VPageAddress = AREASA(sa);
  510. VPageOffset = PAGLEN;
  511. *packed = FALSE;
  512. if (mpsacbinit[sa] > 1L)
  513. { /* If buffer is big enough */
  514. #if EXE386
  515. ra = 0L; /* Offset within segment */
  516. #endif
  517. vaStart = VPageAddress;
  518. vaLast = vaStart;
  519. BufEnd = vaStart + mpsacbinit[sa];
  520. LastB = GetFromVM();
  521. CurrentB = GetFromVM();
  522. proceed = (WORD) TRUE; /* Initialize */
  523. while (proceed)
  524. {
  525. length = ScanWhileDifferent();
  526. if (!(proceed = AfterScanning(length)))
  527. break;
  528. if ((length = ScanWhileSame()) > MINREPEAT)
  529. { /* If there are enough same bytes */
  530. if (vaLast != vaStart)
  531. OutEnum(); /* First write out preceeding diff. bytes */
  532. OutIter(sa, length); /* Now write out iterated record */
  533. proceed = AfterScanning(length);
  534. *packed = (WORD) TRUE;
  535. vaLast = vaStart;
  536. }
  537. else proceed = AfterScanning(length);
  538. /* Otherwise enumerated record swallow this */
  539. } /* small repeated record */
  540. }
  541. if (*packed)
  542. {
  543. if (vaLast != BufEnd)
  544. {
  545. vaStart = BufEnd;
  546. OutEnum(); /* Write out any remainig bytes */
  547. }
  548. mpsacbinit[sa] = ftell(bsRunfile) - lfaStart;
  549. /* Return number of written bytes */
  550. return(lfaStart);
  551. }
  552. else
  553. return(OutVm(AREASA(sa),mpsacbinit[sa]));
  554. }
  555. #endif /*FEXEPACK AND OSEGEXE*/