summaryrefslogtreecommitdiff
path: root/src/unexenix.c
diff options
context:
space:
mode:
authorJim Blandy <jimb@redhat.com>1992-01-13 21:48:03 +0000
committerJim Blandy <jimb@redhat.com>1992-01-13 21:48:03 +0000
commitd427b66a664c0e1ffc818dfa5b87b45b4857d2ae (patch)
treea3255be5cf521ab9c44b9fdfd06a0466274be421 /src/unexenix.c
parentb2c9579f172da05112f29b664de6d8da98c1e813 (diff)
downloademacs-d427b66a664c0e1ffc818dfa5b87b45b4857d2ae.tar.gz
entered into RCS
Diffstat (limited to 'src/unexenix.c')
-rw-r--r--src/unexenix.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/src/unexenix.c b/src/unexenix.c
new file mode 100644
index 00000000000..ea6cd7d5c20
--- /dev/null
+++ b/src/unexenix.c
@@ -0,0 +1,262 @@
+/* Unexec for Xenix.
+ Note that the GNU project considers support for Xenix operation
+ a peripheral activity which should not be allowed to divert effort
+ from development of the GNU system. Changes in this code will be
+ installed when Xenix users send them in, but aside from that
+ we don't plan to think about it, or about whether other Emacs
+ maintenance might break it.
+
+ Copyright (C) 1988 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+/*
+ On 80386 Xenix, segmentation screws prevent us from modifying the text
+ segment at all. We basically just plug a new value for "data segment
+ size" into the countless headers and copy the other records straight
+ through. The data segment is ORG'ed at the xs_rbase value of the data
+ segment's xseg record (always @ 0x1880000, thanks to the "sophisticated
+ memory management hardware" of the chip) and extends to sbrk(0), exactly.
+ This code is afraid to malloc (should it be?), and alloca has to be the
+ wimpy, malloc-based version; consequently, data is usually copied in
+ smallish chunks.
+
+ gb@entity.com
+*/
+
+#include "config.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <varargs.h>
+#include <a.out.h>
+
+static void fatal_unexec ();
+
+#define READ(_fd, _buffer, _size, _error_message, _error_arg) \
+ errno = EEOF; \
+ if (read(_fd, _buffer, _size) != _size) \
+ fatal_unexec(_error_message, _error_arg);
+
+#define WRITE(_fd, _buffer, _size, _error_message, _error_arg) \
+ if (write(_fd, _buffer, _size) != _size) \
+ fatal_unexec(_error_message, _error_arg);
+
+#define SEEK(_fd, _position, _error_message, _error_arg) \
+ errno = EEOF; \
+ if (lseek(_fd, _position, L_SET) != _position) \
+ fatal_unexec(_error_message, _error_arg);
+
+extern int errno;
+extern int sys_nerr;
+extern char *sys_errlist[];
+#define EEOF -1
+
+#ifndef L_SET
+#define L_SET 0
+#endif
+
+/* Should check the magic number of the old executable;
+ not yet written. */
+check_exec (x)
+ struct xexec *x;
+{
+}
+
+
+unexec (new_name, a_name, data_start, bss_start, entry_address)
+ char *new_name, *a_name;
+ unsigned data_start, bss_start, entry_address;
+{
+ char *sbrk (), *datalim = sbrk (0), *data_org;
+ long segpos, textseen, textpos, textlen, datapos, datadiff, datalen;
+
+ struct xexec u_xexec, /* a.out header */
+ *u_xexecp = &u_xexec;
+ struct xext u_xext, /* extended header */
+ *u_xextp = &u_xext;
+ struct xseg u_xseg, /* segment table entry */
+ *u_xsegp = &u_xseg;
+ int i, nsegs, isdata = 0, infd, outfd;
+
+ infd = open (a_name, O_RDONLY, 0);
+ if (infd < 0) fatal_unexec ("opening %s", a_name);
+
+ outfd = creat (new_name, 0666);
+ if (outfd < 0) fatal_unexec ("creating %s", new_name);
+
+ READ (infd, u_xexecp, sizeof (struct xexec),
+ "error reading %s", a_name);
+ check_exec (u_xexecp);
+ READ (infd, u_xextp, sizeof (struct xext),
+ "error reading %s", a_name);
+ segpos = u_xextp->xe_segpos;
+ nsegs = u_xextp->xe_segsize / sizeof (struct xseg);
+ SEEK (infd, segpos, "seek error on %s", a_name);
+ for (i = 0; i < nsegs; i ++)
+ {
+ READ (infd, u_xsegp, sizeof (struct xseg),
+ "error reading %s", a_name);
+ switch (u_xsegp->xs_type)
+ {
+ case XS_TTEXT:
+ {
+ if (i == 0)
+ {
+ textpos = u_xsegp->xs_filpos;
+ textlen = u_xsegp->xs_psize;
+ break;
+ }
+ fatal_unexec ("invalid text segment in %s", a_name);
+ }
+ case XS_TDATA:
+ {
+ if (i == 1)
+ {
+ datapos = u_xsegp->xs_filpos;
+ datalen = datalim - (data_org = (char *)(u_xsegp->xs_rbase));
+ datadiff = datalen - u_xsegp->xs_psize;
+ break;
+ }
+ fatal_unexec ("invalid data segment in %s", a_name);
+ }
+ default:
+ {
+ if (i > 1) break;
+ fatal_unexec ("invalid segment record in %s", a_name);
+ }
+ }
+ }
+ u_xexecp->x_data = datalen;
+ u_xexecp->x_bss = 0;
+ WRITE (outfd, u_xexecp, sizeof (struct xexec),
+ "error writing %s", new_name);
+ WRITE (outfd, u_xextp, sizeof (struct xext),
+ "error writing %s", new_name);
+ SEEK (infd, segpos, "seek error on %s", a_name);
+ SEEK (outfd, segpos, "seek error on %s", new_name);
+
+ /* Copy the text segment record verbatim. */
+
+ copyrec (infd, outfd, sizeof (struct xseg), a_name, new_name);
+
+ /* Read, modify, write the data segment record. */
+
+ READ (infd, u_xsegp, sizeof (struct xseg),
+ "error reading %s", a_name);
+ u_xsegp->xs_psize = u_xsegp->xs_vsize = datalen;
+ u_xsegp->xs_attr &= (~XS_AITER & ~XS_ABSS);
+ WRITE (outfd, u_xsegp, sizeof (struct xseg),
+ "error writing %s", new_name);
+
+ /* Now copy any additional segment records, adjusting their
+ file position field */
+
+ for (i = 2; i < nsegs; i++)
+ {
+ READ (infd, u_xsegp, sizeof (struct xseg),
+ "error reading %s", a_name);
+ u_xsegp->xs_filpos += datadiff;
+ WRITE (outfd, u_xsegp, sizeof (struct xseg),
+ "error writing %s", new_name);
+ }
+
+ SEEK (infd, textpos, "seek error on %s", a_name);
+ SEEK (outfd, textpos, "seek error on %s", new_name);
+ copyrec (infd, outfd, textlen, a_name, new_name);
+
+ SEEK (outfd, datapos, "seek error on %s", new_name);
+ WRITE (outfd, data_org, datalen,
+ "write error on %s", new_name);
+
+ for (i = 2, segpos += (2 * sizeof (struct xseg));
+ i < nsegs;
+ i++, segpos += sizeof (struct xseg))
+ {
+ SEEK (infd, segpos, "seek error on %s", a_name);
+ READ (infd, u_xsegp, sizeof (struct xseg),
+ "read error on %s", a_name);
+ SEEK (infd, u_xsegp->xs_filpos, "seek error on %s", a_name);
+ /* We should be at eof in the output file here, but we must seek
+ because the xs_filpos and xs_psize fields in symbol table
+ segments are inconsistent. */
+ SEEK (outfd, u_xsegp->xs_filpos + datadiff, "seek error on %s", new_name);
+ copyrec (infd, outfd, u_xsegp->xs_psize, a_name, new_name);
+ }
+ close (infd);
+ close (outfd);
+ mark_x (new_name);
+ return 0;
+}
+
+copyrec (infd, outfd, len, in_name, out_name)
+ int infd, outfd, len;
+ char *in_name, *out_name;
+{
+ char buf[BUFSIZ];
+ int chunk;
+
+ while (len)
+ {
+ chunk = BUFSIZ;
+ if (chunk > len)
+ chunk = len;
+ READ (infd, buf, chunk, "error reading %s", in_name);
+ WRITE (outfd, buf, chunk, "error writing %s", out_name);
+ len -= chunk;
+ }
+}
+
+/*
+ * mark_x
+ *
+ * After succesfully building the new a.out, mark it executable
+ */
+static
+mark_x (name)
+ char *name;
+{
+ struct stat sbuf;
+ int um = umask (777);
+ umask (um);
+ if (stat (name, &sbuf) < 0)
+ fatal_unexec ("getting protection on %s", name);
+ sbuf.st_mode |= 0111 & ~um;
+ if (chmod (name, sbuf.st_mode) < 0)
+ fatal_unexec ("setting protection on %s", name);
+}
+
+static void
+fatal_unexec (s, va_alist)
+ va_dcl
+{
+ va_list ap;
+ if (errno == EEOF)
+ fputs ("unexec: unexpected end of file, ", stderr);
+ else if (errno < sys_nerr)
+ fprintf (stderr, "unexec: %s, ", sys_errlist[errno]);
+ else
+ fprintf (stderr, "unexec: error code %d, ", errno);
+ va_start (ap);
+ _doprnt (s, ap, stderr);
+ fputs (".\n", stderr);
+ exit (1);
+}