source: vendor/emx/current/src/emxstack/emxstack.c

Last change on this file was 18, checked in by bird, 22 years ago

Initial revision

  • Property cvs2svn:cvs-rev set to 1.1
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 15.2 KB
Line 
1/* emxstack.c -- Fix the stack size
2 Copyright (c) 1994-1998 Eberhard Mattes
3
4This file is part of emxstack.
5
6emxstack is free software; you can redistribute it and/or modify it
7under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11emxstack is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with emxstack; see the file COPYING. If not, write to
18the Free Software Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA. */
20
21
22#include <stdio.h>
23#include <stdlib.h>
24#include <stdarg.h>
25#include <string.h>
26#include <getopt.h>
27#include <sys/dirtree.h>
28#include "defs.h"
29
30#define VERSION "0.9d"
31
32/* How to open a file. This type is used for my_open(). */
33
34enum open_mode
35{
36 open_read, open_read_write
37};
38
39/* A file. F is the stream. NAME is the name of the file. */
40
41struct file
42{
43 FILE *f;
44 const char *name;
45};
46
47
48/* Mode of operation. */
49
50static enum op
51{
52 op_none, /* No command specified */
53 op_show, /* Display stack size */
54 op_check, /* Check whether fix is required */
55 op_fix, /* Fix stack size */
56 op_update, /* Update stack size */
57 op_force /* Set stack size */
58} operation;
59
60/* This variable is 2 if verbose output is requested (-v), 1 by
61 default, and 0 if emxstack should be quiet (-q). */
62
63static int verbosity;
64
65/* New stack size. */
66
67static long new_stack_size;
68
69/* Minimum stack size. */
70
71static long min_stack_size;
72
73
74/* Combine two 16-bit words (low word L and high word H) into a 32-bit
75 word. */
76
77#define COMBINE(L,H) ((L) | (long)((H) << 16))
78
79/* Round up to the next integral multiple of the page size. */
80
81#define ROUND_PAGE(X) ((((X) - 1) & ~0xfff) + 0x1000)
82
83
84static void error (const char *fmt, ...) NORETURN2;
85
86
87/* Print an error message and abort. This function is called like
88 printf() but never returns. The message will be prefixed with
89 "emxbind: " and a newline will be added at the end. */
90
91static void error (const char *fmt, ...)
92{
93 va_list arg_ptr;
94
95 va_start (arg_ptr, fmt);
96 fprintf (stderr, "emxstack: ");
97 vfprintf (stderr, fmt, arg_ptr);
98 fputc ('\n', stderr);
99 exit (2);
100}
101
102
103/* Print an error message and abort. This function is called like
104 printf() but never returns. The message will be prefixed with the
105 file name NAME and a newline will be added at the end. */
106
107static void file_error (const char *fmt, const char *name, ...)
108{
109 va_list arg_ptr;
110
111 va_start (arg_ptr, name);
112 fprintf (stderr, "%s: ", name);
113 vfprintf (stderr, fmt, arg_ptr);
114 fputc ('\n', stderr);
115 exit (2);
116}
117
118
119/* Display a warning message. If the -c or -f commands are used
120 without -v, the message is supressed. */
121
122static void file_warning (const char *fmt, const char *name, ...)
123{
124 va_list arg_ptr;
125
126 if (verbosity >= 2 || (operation != op_check && operation != op_fix))
127 {
128 va_start (arg_ptr, name);
129 fprintf (stderr, "%s: ", name);
130 vfprintf (stderr, fmt, arg_ptr);
131 fputc ('\n', stderr);
132 }
133}
134
135
136/* Read SIZE bytes from the file F to the buffer DST. If there aren't
137 SIZE bytes at the current location of the read/write pointer, an
138 error message will be displayed and the program will be
139 terminated. */
140
141static void my_read (void *dst, size_t size, struct file *f)
142{
143 if (fread (dst, 1, size, f->f) != size)
144 {
145 if (ferror (f->f))
146 file_error ("%s", f->name, strerror (errno));
147 else
148 file_error ("End of file reached", f->name);
149 }
150}
151
152
153/* Write SIZE bytes from SRC to the file F. */
154
155static void my_write (const void *src, size_t size, struct file *f)
156{
157 if (fwrite (src, 1, size, f->f) != size)
158 file_error ("%s", f->name, strerror (errno));
159}
160
161
162/* Open the file named NAME and let F reference that file. OMODE is
163 the open mode. The file is always opened in binary mode. */
164
165static void my_open (struct file *f, const char *name, enum open_mode omode)
166{
167 const char *ms;
168
169 switch (omode)
170 {
171 case open_read:
172 ms = "rb";
173 break;
174 case open_read_write:
175 ms = "r+b";
176 break;
177 default:
178 ms = NULL;
179 break;
180 }
181 f->name = name;
182 f->f = fopen (name, ms);
183 if (f->f == NULL)
184 file_error ("%s", name, strerror (errno));
185}
186
187
188/* Close the file F if it is open. */
189
190static void my_close (struct file *f)
191{
192 if (fflush (f->f) != 0 || fclose (f->f) != 0)
193 file_error ("%s", f->name, strerror (errno));
194}
195
196
197/* Set the read/write pointer of the file F to location POS. */
198
199static void my_seek (struct file *f, long pos)
200{
201 if (fseek (f->f, pos, SEEK_SET) != 0)
202 file_error ("%s", f->name, strerror (errno));
203}
204
205
206/* Return the length of the file F. Note that this function moves the
207 read/write pointer to the end of the file. */
208
209static long my_size (struct file *f)
210{
211 long n;
212
213 if (fseek (f->f, 0L, SEEK_END) != 0 || (n = ftell (f->f)) < 0)
214 error ("seek failed (`%s')", f->name);
215 return n;
216}
217
218
219/* Tell the user how to use this program. */
220
221static void usage (void)
222{
223 fputs ("emxstack " VERSION " -- "
224 "Copyright (c) 1994-1995 by Eberhard Mattes\n\n", stderr);
225 fputs ("Usage: emxstack <command> [<options>] <file>...\n", stderr);
226 fputs ("\nCommands:\n", stderr);
227 fputs ("-c Check whether stack size should be fixed\n", stderr);
228 fputs ("-d Display stack size\n", stderr);
229 fputs ("-f Fix stack size for emx 0.9\n", stderr);
230 fputs ("-s<n> Set stack size to <n> Kbyte\n", stderr);
231 fputs ("-u<n> Update stack size to <n> Kbyte (if less than <n> Kbyte)\n",
232 stderr);
233 fputs ("\nOptions:\n", stderr);
234 fputs ("-p Act on all files in PATH\n", stderr);
235 fputs ("-q Be quiet\n", stderr);
236 fputs ("-v Be verbose\n", stderr);
237 exit (1);
238}
239
240
241/* Process the file F. */
242
243static void process_file (struct file *f)
244{
245 struct exe1_header h1;
246 struct exe2_header h2;
247 struct os2_header ho;
248 struct object stack_obj, temp_obj;
249 long off_new_exe, off_stack_obj, size;
250 long stack_size, stack_kb;
251 dword i;
252 int uses_emx;
253 byte buf[2];
254
255 /* Read the DOS EXE header. */
256
257 size = my_size (f);
258 if (size < sizeof (h1))
259 {
260 file_warning ("Not an executable file", f->name);
261 return;
262 }
263 my_seek (f, 0);
264 my_read (&h1, sizeof (h1), f);
265 if (h1.magic != 0x5a4d)
266 {
267 file_warning ("Not an executable file", f->name);
268 return;
269 }
270
271 /* Read the second part of the DOS EXE header. */
272
273 if (size < sizeof (h1) + sizeof (h2)
274 || h1.reloc_ptr < sizeof (h1) + sizeof (h2))
275 {
276 file_warning ("DOS executable", f->name);
277 return;
278 }
279 my_read (&h2, sizeof (h2), f);
280 off_new_exe = COMBINE (h2.new_lo, h2.new_hi);
281
282 /* Read the magic word of the new EXE header. */
283
284 if (off_new_exe + 2 > size)
285 {
286 file_warning ("DOS executable", f->name);
287 return;
288 }
289 my_seek (f, off_new_exe);
290 my_read (buf, 2, f);
291 if (!(buf[0] == 'L' && buf[1] == 'X'))
292 {
293 file_warning ("Not an LX executable file", f->name);
294 return;
295 }
296
297 /* Read the LX header. */
298
299 my_seek (f, off_new_exe);
300 my_read (&ho, sizeof (ho), f);
301
302 /* Skip dynamic link libraries; they don't have a stack object. */
303
304 if (ho.mod_flags & 0x8000)
305 {
306 file_warning ("Dynamic link library", f->name);
307 return;
308 }
309
310 /* Retrieve the stack size from the object-relative initial value of
311 ESP. We ignore the stack_size field of the LX header -- it is
312 not set by emxbind and was not set in older versions of
313 LINK386. */
314
315 stack_size = ho.stack_esp;
316 stack_kb = stack_size / 1024;
317
318 /* Check whether the object number for ESP is valid. */
319
320 if (ho.stack_obj < 1 || ho.stack_obj > ho.obj_count)
321 file_error ("Invalid stack object number", f->name);
322
323 /* Read the object record for the stack. */
324
325 off_stack_obj = (off_new_exe + ho.obj_offset
326 + (ho.stack_obj - 1) * sizeof (struct object));
327 my_seek (f, off_stack_obj);
328 my_read (&stack_obj, sizeof (stack_obj), f);
329
330 /* Scan the import module table to check whether the executable
331 imports EMX.DLL, EMXLIBC.DLL, or EMXWRAP.DLL. If at least one of
332 these DLLs is imported, `uses_emx' is set to true. */
333
334 uses_emx = 0;
335 my_seek (f, off_new_exe + ho.impmod_offset);
336 for (i = 0; i < ho.impmod_count; ++i)
337 {
338 byte len, name[255+1];
339
340 my_read (&len, 1, f);
341 if (len != 0)
342 my_read (name, len, f);
343 name[len] = 0;
344 if (stricmp (name, "EMX") == 0 || stricmp (name, "EMXLIBC") == 0
345 || stricmp (name, "EMXWRAP") == 0)
346 {
347 uses_emx = TRUE;
348 break;
349 }
350 }
351
352 /* Now perform the action requested by the user. */
353
354 switch (operation)
355 {
356 case op_show:
357 printf ("%s: %lu Kbyte\n", f->name, stack_kb);
358 break;
359
360 case op_check:
361 if (!uses_emx)
362 file_warning ("Does not use emx.dll", f->name);
363 else if (stack_size <= 16384)
364 printf ("%s: Needs to be fixed\n", f->name);
365 else if (verbosity >= 2)
366 printf ("%s: Does not need to be fixed\n", f->name);
367 break;
368
369 case op_fix:
370 if (!uses_emx)
371 {
372 file_warning ("Does not use emx.dll", f->name);
373 break;
374 }
375 /*NOBREAK*/
376
377 case op_update:
378 if (stack_size >= min_stack_size)
379 {
380 printf ("%s: Stack size is %lu Kbyte; not changed\n",
381 f->name, stack_kb);
382 break;
383 }
384 /*NOBREAK*/
385
386 case op_force:
387
388 /* Changing the stack size when there are pages assigned to the
389 stack object or any succeeding non-resource object is
390 difficult as the page map must be changed. Changing the
391 stack size when there are any non-resource objects following
392 the stack object is difficult or impossible as relocations
393 may have to be changed or invented. Therefore, we fail if
394 there are pages assigned to the stack object or if any
395 non-resource objects follow the stack object. */
396
397 if (stack_obj.map_count != 0)
398 {
399 file_warning ("Stack object has page map entries"
400 " -- stack size not changed", f->name);
401 return;
402 }
403
404 my_seek (f, off_stack_obj + sizeof (struct object));
405 for (i = ho.stack_obj + 1; i <= ho.obj_count; ++i)
406 {
407 my_read (&temp_obj, sizeof (temp_obj), f);
408 if (!(temp_obj.attr_flags & 0x08))
409 {
410 file_warning ("A non-resource object succeeds the stack object"
411 " -- stack size not changed", f->name);
412 return;
413 }
414 }
415
416 if (verbosity >= 2)
417 printf ("%s: Stack size was %lu Kbyte\n", f->name, stack_kb);
418
419 /* Update the initial value of ESP and the size of the stack
420 object. */
421
422 ho.stack_esp = new_stack_size;
423 stack_obj.virt_size = ROUND_PAGE (new_stack_size);
424
425 /* Set the stack_size field to zero -- it is not used. */
426
427 ho.stack_size = 0;
428
429 /* Write the modified LX header. */
430
431 my_seek (f, off_new_exe);
432 my_write (&ho, sizeof (ho), f);
433
434 /* Write the modified stack object record. */
435
436 my_seek (f, off_stack_obj);
437 my_write (&stack_obj, sizeof (stack_obj), f);
438 break;
439
440 default:
441 abort ();
442 }
443}
444
445
446/* Process the file FNAME. */
447
448static void process_fname (const char *fname)
449{
450 struct file f;
451
452 switch (operation)
453 {
454 case op_show:
455 case op_check:
456 my_open (&f, fname, open_read);
457 break;
458
459 case op_update:
460 case op_force:
461 my_open (&f, fname, open_read_write);
462 break;
463
464 default:
465 abort ();
466 }
467 process_file (&f);
468 my_close (&f);
469}
470
471
472/* Process all files in directory NAME. */
473
474static void process_dir (const char *name)
475{
476 struct _dt_tree *tree;
477 struct _dt_node *p;
478 char path[512], *s;
479
480 tree = _dt_read (name, "*.exe", 0);
481 if (tree == NULL)
482 {
483 file_warning ("Cannot open directory", name);
484 return;
485 }
486 strcpy (path, name);
487 s = strchr (path, 0);
488 if (s != path && strchr ("\\:/", s[-1]) == NULL)
489 *s++ = '\\';
490 for (p = tree->tree; p != NULL; p = p->next)
491 if (!(p->attr & 0x10))
492 {
493 strcpy (s, p->name);
494 process_fname (path);
495 }
496 _dt_free (tree);
497}
498
499
500/* Process all directories listed in LIST. */
501
502static void process_env (const char *list)
503{
504 char dir[512];
505 const char *end;
506 int i;
507
508 if (list == NULL || *list == 0)
509 return;
510 for (;;)
511 {
512 while (*list == ' ' || *list == '\t') ++list;
513 if (*list == 0) break;
514 end = list;
515 while (*end != 0 && *end != ';') ++end;
516 i = end - list;
517 while (i>0 && (list[i-1] == ' ' || list[i-1] == '\t')) --i;
518 if (i != 0)
519 {
520 memcpy (dir, list, i);
521 dir[i] = 0;
522 process_dir (dir);
523 }
524 if (*end == 0) break;
525 list = end + 1;
526 }
527}
528
529
530/* Set the mode of operation to X. */
531
532static void set_op (enum op x)
533{
534 if (operation != op_none)
535 error ("More than one operation specified");
536 operation = x;
537}
538
539
540/* Get the new stack size from the argument of the option currently
541 being parsed. */
542
543static void get_new_stack_size (void)
544{
545 long n;
546 char *e;
547
548 errno = 0;
549 n = strtol (optarg, &e, 0);
550 if (errno != 0 || n < 20 || n > 512*1024 || e == optarg || *e != 0)
551 error ("invalid stack size specified");
552 new_stack_size = ROUND_PAGE (n * 1024);
553}
554
555
556/* The main function of emxstack. */
557
558int main (int argc, char *argv[])
559{
560 int c;
561 int path;
562
563 /* Expand wildcards. */
564
565 _wildcard (&argc, &argv);
566
567 /* Set default values. */
568
569 verbosity = 1; path = FALSE;
570 new_stack_size = 0;
571 min_stack_size = 0;
572 operation = op_none;
573
574 /* Let getopt() display error messages. */
575
576 opterr = TRUE;
577 optswchar = "-";
578 optind = 0;
579
580 /* Display usage information when called without arguments. */
581
582 if (argc < 2 || strcmp (argv[1], "-?") == 0)
583 usage ();
584
585 /* Parse the command line options. */
586
587 while ((c = getopt (argc, argv, "cdfpqs:u:v")) != EOF)
588 switch (c)
589 {
590 case 'c':
591 set_op (op_check);
592 break;
593
594 case 'd':
595 set_op (op_show);
596 break;
597
598 case 'f':
599 set_op (op_update);
600 min_stack_size = 17 * 1024;
601 new_stack_size = 8 * 1024 * 1024;
602 break;
603
604 case 'p':
605 path = TRUE;
606 break;
607
608 case 's':
609 set_op (op_force);
610 get_new_stack_size ();
611 min_stack_size = 0;
612 break;
613
614 case 'u':
615 set_op (op_update);
616 get_new_stack_size ();
617 min_stack_size = new_stack_size;
618 break;
619
620 case 'q':
621 verbosity = 0;
622 break;
623
624 case 'v':
625 verbosity = 2;
626 break;
627
628 default:
629 exit (1);
630 }
631
632 if (operation == op_none)
633 error ("No operation given on the command line");
634
635 if (path)
636 {
637 /* Use PATH. */
638
639 if (optind < argc)
640 error ("No files must be given on the command line if -p is used");
641 process_env (getenv ("PATH"));
642 }
643
644 else
645 {
646 /* Process the remaining arguments as files. */
647
648 int i;
649
650 i = optind;
651 if (i >= argc)
652 error ("No files given on the command line");
653 while (i < argc)
654 process_fname (argv[i++]);
655 }
656 return 0;
657}
Note: See TracBrowser for help on using the repository browser.