1 /* aout.c - parse and load the contents of a UNIX a.out file, for
2 * several flavours of PDP-11 UNIX
5 * $Date: 2008/05/19 13:42:39 $
12 /* Array of 64K for data and instruction space */
13 u_int8_t *ispace, *dspace; /* Instruction and Data spaces */
14 static u_int8_t darray[PDP_MEM_SIZE], iarray[PDP_MEM_SIZE];
17 /* 2.11BSD allows up to 16 8K overlays in the 0430 and 0431 a.out types.
18 * Each overlay is loaded at the first 8K `click' above the end of the
19 * main text. The following structures hold the overlays from the current
20 * a.out, if there are any. Missing overlays have size 0 and pointer NULL.
59 static u_int8_t *ovbase; /* Base address of 2.11BSD overlays */
60 u_int32_t ov_changes = 0; /* Number of overlay changes */
61 u_int8_t current_ov = 0; /* Current overlay number */
64 /* Global array of pointers to arguments and environment. This
65 * allows load_a_out() to modify it when dealing with shell
66 * scripts, before calling set_arg_env()
68 char *Argv[MAX_ARGS], *Envp[MAX_ARGS];
70 int Binary; /* Type of binary this a.out is */
72 /* For programs without an environment, we set the environment statically.
73 * Eventually there will be code to get some environment variables
75 static char *default_envp[4] = {
76 "PATH=/bin:/usr/bin:/usr/sbin:/usr/ucb:/usr/games:/usr/local/bin:.",
82 static int default_envc = 4;
85 static void set_arg_env(int want_env);
88 /* Load the a.out header from the given file pointer, and return it.
89 * Also return an integer describing which version of UNIX the a.out
90 * belongs to. If errors on reading, return -1.
92 int load_aout_header(FILE * zin, struct exec *E)
96 /* Read the a_magic value first */
97 /* This makes it easier to deal with */
98 /* parsing any script interpreter below */
99 if (fread(E, sizeof(u_int16_t), 1, zin) != 1)
102 switch (E->a_magic) {
103 case ANY_SCRIPT: /* Shell script, return now */
106 case ANY_NORMAL: /* These are recognised below */
114 default: /* Unrecognised binary, mark as such */
115 E->a_magic = UNKNOWN_AOUT;
119 /* We can deal with this a.out, so */
120 /* read in the rest of the header */
121 cptr = (char *) &(E->a_text);
122 if (fread(cptr, 1, sizeof(struct exec) - sizeof(u_int16_t), zin)
123 < 16 - sizeof(u_int16_t))
126 switch (E->a_magic) {
128 if (E->a_data == A68_DATA)
131 E->a_magic = UNKNOWN_AOUT;
141 case ANY_SPLITID: /* Check crt0.o 2nd magic for V2/V6/V7/2.11BSD */
142 if (E->a_magic2 == V2_M2)
144 if (E->a_magic2 == V6_M2)
146 if (E->a_magic2 == V7_M2)
148 if (E->a_magic2 == BSD_M2)
151 /* Still no idea, use checksum to determine */
152 return (special_magic((u_int16_t *) E));
154 default: /* Should never get here */
155 E->a_magic = UNKNOWN_AOUT;
161 /* Read in the executable name and its arguments from the shell script,
162 * and the re-call load_a_out to load in that binary. Returns 0 on
163 * success, -1 on error. Input file is always closed by this routine.
165 int load_script(const char *file, const char *origpath, FILE * zin,
168 #define SCRIPT_LINESIZE 512 /* Max size of 1st line in script */
170 char *script_arg[MAX_ARGS];
171 int i, script_cnt = 0;
174 for (i = 0; i < Argc; i++)
175 TrapDebug((dbg_file, "In load_script Argv[%d] is %s\n", i,
177 /* Get the first line of the file */
178 if (((script_line = (char *) malloc(SCRIPT_LINESIZE)) == NULL) ||
179 (fgets(script_line, SCRIPT_LINESIZE, zin) == NULL)) {
180 (void) fprintf(stderr,
181 "Apout - could not read 1st line of script\n");
185 /* Now break into separate words */
186 for (ap = script_arg; (*ap = strsep(&script_line, " \t\n")) != NULL;)
190 if (script_cnt >= MAX_ARGS)
193 if (fclose(zin) != 0) {
198 TrapDebug((dbg_file, "Script: extra args are is %d\n", script_cnt));
200 for (i = 0; i < script_cnt; i++)
201 fprintf(dbg_file, " script_arg[%d] is %s\n", i, script_arg[i]);
205 /* Ensure we have room to shift the args */
206 if ((Argc + script_cnt) > MAX_ARGS) {
207 (void) fprintf(stderr, "Apout - out of argv space in script\n");
211 /* Now shift the args up and insert new ones */
212 for (i = Argc - 1; i != 0; i--)
213 Argv[i + script_cnt] = Argv[i];
214 for (i = 0; i < Argc; i++)
215 TrapDebug((dbg_file, "Part A load_script Argv[%d] is %s\n", i,
217 for (i = 0; i < script_cnt; i++)
218 Argv[i] = script_arg[i];
219 if (origpath != NULL)
220 Argv[i] = strdup(origpath);
222 Argv[i] = strdup(file);
224 for (i = 0; i < Argc; i++)
225 TrapDebug((dbg_file, "Part B load_script Argv[%d] is %s\n", i,
228 file = xlate_filename(script_arg[0]);
230 for (i = 0; i < Argc; i++)
231 TrapDebug((dbg_file, "Leaving load_script Argv[%d] is %s\n", i,
233 return (load_a_out(file, origpath, want_env));
237 /* Load the named PDP-11 executable file into the emulator's memory.
238 * Returns 0 if ok, -1 if error. Also initialise the simulator and set
239 * up the stack for the process with Argc, Argv, Envc, Envp.
240 * origpath is the path to the executable as seen by the simulated
241 * parent, or NULL if this is not known.
243 int load_a_out(const char *file, const char *origpath, int want_env)
246 /* @globals errno,stdout,stderr; @ */
247 #define V12_MEMBASE 16384 /* Offset for V1/V2 binaries load */
250 u_int8_t *ibase, *dbase, *bbase; /* Instruction, data, bss bases */
260 for (i = 0; i < Argc; i++)
261 TrapDebug((dbg_file, "In load_a_out Argv[%d] is %s\n", i,
264 (void) signal(SIGBUS, bus_error); /* Catch all bus errors here */
266 if ((zin = fopen(file, "r")) == NULL) /* Open the file */
269 Binary = load_aout_header(zin, &e); /* Determine a.out & Unix type */
271 if (e.a_magic == ANY_SCRIPT) { /* Shell script, run that */
272 return (load_script(file, origpath, zin, want_env));
275 if (Binary == IS_211BSD) {
276 (void) fprintf(stderr,
277 "Apout not compiled to support 2.11BSD binaries\n");
283 if (Binary == IS_V1) {
284 (void) fprintf(stderr,
285 "Apout not compiled to support 1st Edition binaries\n");
289 if (Binary == IS_V2) {
290 (void) fprintf(stderr,
291 "Apout not compiled to support 2nd Edition binaries\n");
298 /* Executable was not recognised.
299 * Try to exec it as a native binary.
300 * If it fails, doesn't matter. If it
301 * succeeds, then great. This allows
302 * us to have mixed native and PDP-11
303 * binaries in the same filespace.
305 if (e.a_magic == UNKNOWN_AOUT) {
307 TrapDebug((dbg_file, "About to try native exec on %s\n", file));
311 execv(file, Argv); /* envp[] is the one Apout's main() got */
312 TrapDebug((dbg_file, "Nope, didn't work\n"));
316 /* Try to run it as a V1 raw binary */
318 TrapDebug((dbg_file, "About to try PDP-11 raw exec on %s\n",
322 if ((zin = fopen(file, "r")) == NULL) /* reopen the file */
327 (void) fprintf(stderr, "Apout - unknown a.out file %s\n", file);
329 #endif /* RUN_V1_RAW */
331 /* Now we know what environment to
332 * create, set up the memory areas
333 * according to the magic numbers
338 TrapDebug((dbg_file, "A68 binary\n"));
341 TrapDebug((dbg_file, "V1 binary\n"));
344 TrapDebug((dbg_file, "V2 binary\n"));
347 TrapDebug((dbg_file, "V5 binary\n"));
350 TrapDebug((dbg_file, "V6 binary\n"));
353 TrapDebug((dbg_file, "V7 binary\n"));
356 TrapDebug((dbg_file, "2.11BSD binary\n"));
364 if (fseek(zin, 0, SEEK_SET) != 0) {
368 ispace = dspace = darray;
369 ibase = &(ispace[V12_MEMBASE]); /* Load & run the binary starting */
370 dbase = ibase; /* at address 16384 (040000) */
372 e.a_entry = V12_MEMBASE;
373 /* Reset the exec header fields to make loading code below
376 if (stat(file, &stb)) {
377 fprintf(stderr, "Apout - cannot stat %s\n", file);
380 e.a_text = stb.st_size;
381 bbase = &(ispace[V12_MEMBASE + e.a_text]);
386 case V1_NORMAL: /* V1 a.out binary looks like */
387 e.a_bss = e.a_syms; /* 0405 */
388 e.a_syms = e.a_data; /* size of text */
389 e.a_data = 0; /* size of symbol table */
391 /* size of data (i.e bss) */
392 /* unused and zeroed */
393 /* We must rearrange fields */
394 /* Move back to start of V1 header */
395 if (fseek(zin, 0, SEEK_SET) != 0) {
399 ispace = dspace = darray;
400 ibase = &(ispace[V12_MEMBASE]); /* Load & run the binary starting */
401 dbase = &(ispace[e.a_text]); /* at address 16384 (040000) */
402 bbase = &(ispace[e.a_text + e.a_data]);
404 e.a_entry = V12_MEMBASE;
407 case A68_MAGIC: /* Algol 68 image */
408 if (fseek(zin, 0, SEEK_SET) != 0) {
412 e.a_text = e.ov_siz[0] + 1;
414 e.a_bss = 0160000 - e.a_text;
415 e.a_entry = e.a_flag;
416 ibase = ispace = dspace = darray;
419 bbase = &(ispace[e.a_text + e.a_data]);
422 /* Move back to end of V5/6/7 header */
423 if (fseek(zin, 16, SEEK_SET) != 0) {
427 ibase = ispace = dspace = darray;
429 if (Binary == IS_V2) {
430 ibase = &(ispace[V12_MEMBASE]);
431 e.a_entry = V12_MEMBASE;
432 dbase = &(ispace[e.a_text + V12_MEMBASE]);
433 bbase = &(ispace[e.a_text + e.a_data + V12_MEMBASE]);
437 dbase = &(ispace[e.a_text]);
438 bbase = &(ispace[e.a_text + e.a_data]);
440 if ((Binary < IS_V7))
443 dwrite_base = e.a_text;
446 /* Move back to end of V5/6/7 header */
447 if (fseek(zin, 16, SEEK_SET) != 0) {
453 /* Round up text area to next 8K boundary */
454 if (e.a_text % EIGHT_K) {
455 size = EIGHT_K * (1 + e.a_text / EIGHT_K);
458 /* And the next 8K boundary if overlays! */
459 if (e.a_magic == BSD_OVERLAY) {
460 if (e.max_ovl % EIGHT_K) {
461 size += EIGHT_K * (1 + e.max_ovl / EIGHT_K);
465 ibase = ispace = dspace = darray;
466 dbase = &(ispace[size]);
467 bbase = &(ispace[size + e.a_data]);
471 /* Move back to end of V5/6/7 header */
472 if (fseek(zin, 16, SEEK_SET) != 0) {
478 ibase = ispace = iarray;
479 dbase = dspace = darray;
480 bbase = &(dspace[e.a_data]);
482 /* Try to stop null refs */
483 if (Binary == IS_211BSD)
489 (void) fprintf(stderr, "Apout - unknown a.out format 0%o\n",
496 /* Initialise the instruction table for our environment */
500 for (i = 548; i < 552; i++)
507 for (i = 544; i < 548; i++)
509 for (i = 548; i < 552; i++)
514 for (i = 544; i < 552; i++)
520 for (i = 548; i < 552; i++)
524 fprintf(stderr, "Apout - unknown Unix version for %s\n", file);
529 memset(darray, 0, PDP_MEM_SIZE); /* Clear all memory */
530 if (ispace != dspace)
531 memset(iarray, 0, PDP_MEM_SIZE);
534 /* Now load the text into ibase */
535 for (size = e.a_text; size;) {
536 i = (int) fread(ibase, 1, (size_t) size, zin);
546 /* Now deal with any overlays */
547 if (Binary == IS_211BSD)
551 /* Round up text area to next 8K boundary */
552 if (e.a_text % EIGHT_K) {
553 size = EIGHT_K * (1 + e.a_text / EIGHT_K);
556 ovbase = &ispace[size];
558 for (i = 0; i < NOVL; i++) {
559 if (e.ov_siz[i] == 0) {
561 ovlist[i].ovlay = NULL;
564 /* Create memory for the overlay */
565 ovlist[i].size = e.ov_siz[i];
567 free(ovlist[i].ovlay);
568 ovlist[i].ovlay = (u_int8_t *) malloc(e.ov_siz[i]);
569 if (ovlist[i].ovlay == NULL) {
570 fprintf(stderr, "Apout - can't malloc overlay!\n");
573 /* Load the overlay into memory */
574 for (size = ovlist[i].size, ibase = ovlist[i].ovlay;
576 j = fread(ibase, 1, size, zin);
586 /* And deal with the emt instructions */
587 for (i = 544; i < 548; i++)
588 itab[i] = do_bsd_overlay;
592 /* Now load the data into dbase */
594 for (size = e.a_data; size;) {
595 i = (int) fread(dbase, 1, (size_t) size, zin);
604 /* Now clear the bss */
605 if ((bbase != 0) && (e.a_bss != 0))
606 memset(bbase, 0, (size_t) e.a_bss);
609 /* Set up the registers and flags, and the stack */
612 regs[PC] = e.a_entry;
613 if (Binary == IS_A68) {
617 set_arg_env(want_env);
625 * C runtime startoff. When an a.out is loaded by the kernel, the kernel
626 * sets up the stack as follows:
628 * _________________________________
629 * | (NULL) | 0177776: top of memory
630 * |-------------------------------|
632 * | environment strings |
634 * |-------------------------------|
636 * | argument strings |
638 * |-------------------------------|
639 * | envv[envc] (NULL) | end of environment vector tag, a 0
640 * |-------------------------------|
641 * | envv[envc-1] | pointer to last environment string
642 * |-------------------------------|
644 * |-------------------------------|
645 * | envv[0] | pointer to first environment string
646 * |-------------------------------|
647 * | argv[argc] (NULL) | end of argument vector tag, a 0
648 * |-------------------------------|
649 * | argv[argc-1] | pointer to last argument string
650 * |-------------------------------|
652 * |-------------------------------|
653 * | argv[0] | pointer to first argument string
654 * |-------------------------------|
655 * sp-> | argc | number of arguments
656 * ---------------------------------
658 * Crt0 simply moves the argc down two places in the stack, calculates the
659 * the addresses of argv[0] and envv[0], putting those values into the two
660 * spaces opened up to set the stack up as main expects to see it.
662 * If want_env is set, create a stack by including environment variables:
663 * used by V7, 2.9BSD, 2.11BSD. Otherwise, don't create environment
664 * variables: used by V1 up to V6.
666 static void set_arg_env(int want_env)
673 /* Set default environment if there is none */
674 if (Envp[0] == NULL) {
676 for (i = 0; i < Envc; i++)
677 Envp[i] = default_envp[i];
680 /* Set up the program's name -- used for debugging */
683 progname = strdup(Argv[0]);
686 fprintf(dbg_file, "In set_arg_env, Argc is %d\n", Argc);
687 for (i = 0; i < Argc; i++)
688 fprintf(dbg_file, " Argv[%d] is %s\n", i, Argv[i]);
689 for (i = 0; i < Envc; i++)
690 fprintf(dbg_file, " Envp[%d] is %s\n", i, Envp[i]);
694 /* Now build the arguments and pointers on the stack */
697 if ((Binary == IS_V1) || (Binary == IS_V2))
698 posn = KE11LO - 2; /* Start below the KE11A */
701 posn = PDP_MEM_SIZE - 2;
702 sl_word(posn, 0); /* Put a NULL on top of stack */
705 for (i = Envc - 1; i != -1; i--) { /* For each env string */
706 len = strlen(Envp[i]) + 1; /* get its length */
708 memcpy(&dspace[posn], Envp[i], (size_t) len);
712 for (i = Argc - 1; i != -1; i--) { /* For each arg string */
713 len = strlen(Argv[i]) + 1; /* get its length */
715 memcpy(&dspace[posn], Argv[i], (size_t) len);
719 sl_word(posn, 0); /* Put a NULL at end of env array */
721 if (want_env == 1) { /* For each env string */
722 for (i = Envc - 1; i != -1; i--) {
723 posn -= 2; /* put a pointer to the string */
724 sl_word(posn, (u_int16_t) eposn[i]);
728 /* Put a NULL or -1 before arg ptrs */
734 for (i = Argc - 1; i != -1; i--) { /* For each arg string */
736 sl_word(posn, (u_int16_t) aposn[i]); /* put a ptr to the string */
739 sl_word(posn, (u_int16_t) Argc); /* Save the count of args */
740 regs[SP] = (u_int16_t) posn; /* and initialise the SP */
748 /* This function probably belongs in bsdtrap.c, but all the vars are
751 * Deal with overlay changes which come in via an emt instruction.
754 void do_bsd_overlay()
756 int ov = regs[0] - 1;
758 if (ovlist[ov].size == 0) {
759 fprintf(stderr, "Apout - can't switch to empty overlay %d\n", ov);
762 JsrDebug((dbg_file, "switching to overlay %d\n", ov));
764 /* Memcpy overlay into main ispace */
765 memcpy(ovbase, ovlist[ov].ovlay, ovlist[ov].size);