summaryrefslogtreecommitdiff
path: root/elksemu/elks.c
diff options
context:
space:
mode:
Diffstat (limited to 'elksemu/elks.c')
-rw-r--r--elksemu/elks.c258
1 files changed, 258 insertions, 0 deletions
diff --git a/elksemu/elks.c b/elksemu/elks.c
new file mode 100644
index 0000000..f84c994
--- /dev/null
+++ b/elksemu/elks.c
@@ -0,0 +1,258 @@
+/*
+ * ELKSEMU An emulator for Linux8086 binaries.
+ *
+ * VM86 is used to process all the 8086 mode code.
+ * We trap up to 386 mode for system call emulation and naughties.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/vm86.h>
+#include <sys/mman.h>
+#include "elks.h"
+
+volatile struct vm86_struct elks_cpu;
+unsigned char *elks_base; /* Paragraph aligned */
+
+#define dbprintf(x) db_printf x
+/**/
+
+static void elks_init()
+{
+ elks_cpu.screen_bitmap=0;
+ elks_cpu.cpu_type = CPU_286;
+ /*
+ * All INT xx calls are trapped.
+ */
+ memset((void *)&elks_cpu.int_revectored,0xFF, sizeof(elks_cpu.int_revectored));
+}
+
+static void elks_take_interrupt(int arg)
+{
+ if(arg!=0x80)
+ {
+ dbprintf(("Took an int %d\n", arg));
+ fflush(stderr);
+ kill(getpid(), SIGILL);
+ return;
+ }
+
+ dbprintf(("syscall AX=%X BX=%X CX=%X DX=%x\n",
+ (unsigned short)elks_cpu.regs.eax,
+ (unsigned short)elks_cpu.regs.ebx,
+ (unsigned short)elks_cpu.regs.ecx,
+ (unsigned short)elks_cpu.regs.edx));
+
+ elks_cpu.regs.eax = elks_syscall();
+ dbprintf(("elks syscall returned %d\n", elks_cpu.regs.eax));
+ /* Finally return to vm86 state */
+}
+
+
+static int load_elks(int fd)
+{
+ /* Load the elks binary image and set it up in a suitable VM86 segment. Load CS and DS/SS
+ according to image type. chmem is ignored we always use 64K segments */
+ struct elks_exec_hdr mh;
+ unsigned char *dsp;
+ if(read(fd, &mh,sizeof(mh))!=sizeof(mh))
+ return -ENOEXEC;
+ if(mh.hlen!=EXEC_HEADER_SIZE)
+ return -ENOEXEC;
+ if(mh.type!=ELKS_COMBID&&mh.type!=ELKS_SPLITID)
+ return -ENOEXEC;
+#if 0
+ fprintf(stderr,"Linux-86 binary - %lX. tseg=%ld dseg=%ld bss=%ld\n",
+ mh.type,mh.tseg,mh.dseg,mh.bseg);
+#endif
+ if(read(fd,elks_base,mh.tseg)!=mh.tseg)
+ return -ENOEXEC;
+ if(mh.type==ELKS_COMBID)
+ dsp=elks_base+mh.tseg;
+ else
+ dsp=elks_base+65536;
+ if(read(fd,dsp,mh.dseg)!=mh.dseg)
+ return -ENOEXEC;
+ memset(dsp+mh.dseg,0, mh.bseg);
+ /*
+ * Load the VM86 registers
+ */
+
+/* if(mh.type==ELKS_COMBID)
+ dsp=elks_base;*/
+ elks_cpu.regs.ds=PARAGRAPH(dsp);
+ elks_cpu.regs.es=PARAGRAPH(dsp);
+ elks_cpu.regs.ss=PARAGRAPH(dsp);
+ elks_cpu.regs.esp=65536; /* Args stacked later */
+ elks_cpu.regs.cs=PARAGRAPH(elks_base);
+ elks_cpu.regs.eip=0; /* Run from 0 */
+
+ /*
+ * Loaded, check for sanity.
+ */
+ if( dsp != ELKS_PTR(unsigned char, 0) )
+ {
+ printf("Error VM86 problem %lx!=%lx (Is DS > 16 bits ?)\n",
+ (long)dsp, (long)ELKS_PTR(char, 0));
+ exit(0);
+ }
+
+ return 0;
+}
+
+
+void run_elks()
+{
+ /*
+ * Execute 8086 code for a while.
+ */
+ int err=vm86((struct vm86_struct*)&elks_cpu);
+ switch(VM86_TYPE(err))
+ {
+ /*
+ * Signals are just re-starts of emulation (yes the
+ * handler might alter elks_cpu)
+ */
+ case VM86_SIGNAL:
+ break;
+ case VM86_UNKNOWN:
+ fprintf(stderr, "VM86_UNKNOWN returned\n");
+ exit(1);
+ case VM86_INTx:
+ elks_take_interrupt(VM86_ARG(err));
+ break;
+ case VM86_STI:
+ fprintf(stderr, "VM86_STI returned\n");
+ break; /* Shouldnt be seen */
+ }
+}
+
+void build_stack(char ** argv, char ** envp)
+{
+ char **p;
+ int argv_len=0, argv_count=0;
+ int envp_len=0, envp_count=0;
+ int stack_bytes;
+ unsigned short * pip;
+ unsigned short pcp;
+
+ /* How much space for argv */
+ for(p=argv; *p; p++)
+ {
+ argv_count++; argv_len += strlen(*p)+1;
+ }
+
+ /* How much space for envp */
+ for(p=envp; *p; p++)
+ {
+ envp_count++; envp_len += strlen(*p)+1;
+ }
+
+ /* tot it all up */
+ stack_bytes = 2 /* argc */
+ + argv_count * 2 + 2 /* argv */
+ + argv_len
+ + envp_count * 2 + 2 /* envp */
+ + envp_len;
+
+ /* Allocate it */
+ elks_cpu.regs.esp -= stack_bytes;
+
+/* Sanity check
+ printf("Argv = (%d,%d), Envp=(%d,%d), stack=%d\n",
+ argv_count, argv_len, envp_count, envp_len, stack_bytes);
+*/
+
+ /* Now copy in the strings */
+ pip=ELKS_PTR(unsigned short, elks_cpu.regs.esp);
+ pcp=elks_cpu.regs.esp+2*(1+argv_count+1+envp_count+1);
+
+ *pip++ = argv_count;
+ for(p=argv; *p; p++)
+ {
+ *pip++ = pcp;
+ strcpy(ELKS_PTR(char, pcp), *p);
+ pcp += strlen(*p)+1;
+ }
+ *pip++ = 0;
+
+ for(p=envp; *p; p++)
+ {
+ *pip++ = pcp;
+ strcpy(ELKS_PTR(char, pcp), *p);
+ pcp += strlen(*p)+1;
+ }
+ *pip++ = 0;
+}
+
+void main(int argc, char *argv[], char *envp[])
+{
+ int fd;
+ dbprintf(("ELKSEMU 0.01 Alpha\n"));
+ if(argc==1)
+ {
+ fprintf(stderr,"elksemu cmd args.....\n");
+ exit(1);
+ }
+ elks_init();
+
+ /* The Linux vm will deal with not allocating the unused pages */
+#if __AOUT__
+ /* GNU malloc will align to 4k with large chunks */
+ elks_base = malloc(0x20000);
+#else
+ /* For ELF first 128M is unmapped, it needs to be mapped manually */
+ elks_base = mmap((void*)0x10000, 0x20000,
+ PROT_EXEC|PROT_READ|PROT_WRITE,
+ MAP_ANON|MAP_PRIVATE|MAP_FIXED,
+ 0, 0);
+#endif
+ if( (long)elks_base < 0 || (long)elks_base >= 0xE0000 )
+ {
+ fprintf(stderr, "Elks memory is at an illegal address\n");
+ exit(255);
+ }
+ fd=open(argv[1], O_RDONLY);
+ if(fd==-1)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ if(load_elks(fd) < 0)
+ {
+ fprintf(stderr,"Not a elks binary.\n");
+ exit(1);
+ }
+
+ close(fd);
+
+ build_stack(argv+1, envp);
+
+ while(1)
+ run_elks();
+}
+
+void db_printf(const char * fmt, ...)
+{
+static FILE * db_fd = 0;
+ va_list ptr;
+ int rv;
+ if( db_fd == 0 )
+ {
+ db_fd = fopen("/tmp/ELKS_log", "a");
+ if( db_fd == 0 ) db_fd = stderr;
+ setbuf(db_fd, 0);
+ }
+ fprintf(db_fd, "%d: ", getpid());
+ va_start(ptr, fmt);
+ rv = vfprintf(db_fd,fmt,ptr);
+ va_end(ptr);
+}