Logo Search packages:      
Sourcecode: linux86 version File versions  Download package

elks.c

/*
 *    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 <errno.h>
#include <sys/stat.h>
#include <sys/vm86.h>
#include <sys/mman.h>
#include "elks.h" 

#ifdef __BCC__
#define OLD_LIBC_VERSION
#endif

volatile struct vm86_struct elks_cpu;
unsigned char *elks_base;     /* Paragraph aligned */

#ifdef DEBUG
#define dbprintf(x) db_printf x
#else
#define dbprintf(x) 
#endif

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 1
      if(arg==0x20) { minix_syscall(); return; }
#endif
      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;
#ifdef DEBUG
      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;
}

#ifndef OLD_LIBC_VERSION
  /*
   *  recent versions of libc have changed the proto for vm86()
   *  for now I'll just override ...
   */
#define OLD_SYS_vm86  113
#define NEW_SYS_vm86  166

static inline int vm86_mine(struct vm86_struct* v86)
{
      int __res;
      __asm__ __volatile__("int $0x80\n"
      :"=a" (__res):"a" ((int)OLD_SYS_vm86), "b" ((int)v86));
      return __res;
} 
#endif

void run_elks()
{
      /*
       *    Execute 8086 code for a while.
       */
#ifndef OLD_LIBC_VERSION
      int err=vm86_mine((struct vm86_struct*)&elks_cpu);
#else
      int err=vm86((struct vm86_struct*)&elks_cpu);
#endif
      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;
      struct stat st;
      int ruid, euid, rgid, egid;

      if(argc<=1)
      {
            fprintf(stderr,"elksemu cmd args.....\n");
            exit(1);
      }
      /* This uses the _real_ user ID If the file is exec only that's */
      /* ok cause the suid root will override.  */
      /* BTW, be careful here, security problems are possible because of 
       * races if you change this. */

      if( access(argv[1], X_OK) < 0
        || (fd=open(argv[1], O_RDONLY)) < 0
        || fstat(fd, &st) < 0
        )
      {
            perror(argv[1]);
            exit(1);
      }

      /* Check the suid bits ... */
      ruid = getuid(); rgid = getgid();
      euid = ruid; egid = rgid;
      if( st.st_mode & S_ISUID ) euid = st.st_uid;
      if( st.st_mode & S_ISGID ) egid = st.st_gid;

      /* Set the _real_ permissions, or revoke superuser priviliages */
      setregid(rgid, egid);
      setreuid(ruid, euid);

      dbprintf(("ELKSEMU 0.12.0\n"));
      elks_init();

      /* The Linux vm will deal with not allocating the unused pages */
#if __AOUT__
#if __GNUC__
      /* GNU malloc will align to 4k with large chunks */
      elks_base = malloc(0x20000);
#else
      /* But others won't */
      elks_base = malloc(0x20000+4096);
      elks_base = (void*) (((int)elks_base+4095) & -4096);
#endif
#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);
      }
      
      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();
}

#ifdef DEBUG
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);
}
#endif

Generated by  Doxygen 1.6.0   Back to index