* added compilers lcc and bcc (linux86)
[mascara-docs.git] / compilers / linux86-0.16.17 / elksemu / elks.c
blob3dd3a4df8546f4a464b9b8babaa654931183d80c
1 /*
2 * ELKSEMU An emulator for Linux8086 binaries.
4 * VM86 is used to process all the 8086 mode code.
5 * We trap up to 386 mode for system call emulation and naughties.
6 */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/types.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <stdarg.h>
14 #include <fcntl.h>
15 #include <signal.h>
16 #include <errno.h>
17 #include <sys/stat.h>
18 #include <sys/vm86.h>
19 #include <sys/mman.h>
20 #include "elks.h"
22 #ifdef __BCC__
23 #define OLD_LIBC_VERSION
24 #endif
26 volatile struct vm86_struct elks_cpu;
27 unsigned char *elks_base; /* Paragraph aligned */
29 #ifdef DEBUG
30 #define dbprintf(x) db_printf x
31 #else
32 #define dbprintf(x)
33 #endif
35 static void elks_init()
37 elks_cpu.screen_bitmap=0;
38 elks_cpu.cpu_type = CPU_286;
40 * All INT xx calls are trapped.
42 memset((void *)&elks_cpu.int_revectored,0xFF, sizeof(elks_cpu.int_revectored));
45 static void elks_take_interrupt(int arg)
47 #if 1
48 if(arg==0x20) { minix_syscall(); return; }
49 #endif
50 if(arg!=0x80)
52 dbprintf(("Took an int %d\n", arg));
53 fflush(stderr);
54 kill(getpid(), SIGILL);
55 return;
58 dbprintf(("syscall AX=%x BX=%x CX=%x DX=%x\n",
59 (unsigned short)elks_cpu.regs.eax,
60 (unsigned short)elks_cpu.regs.ebx,
61 (unsigned short)elks_cpu.regs.ecx,
62 (unsigned short)elks_cpu.regs.edx));
64 elks_cpu.regs.eax = elks_syscall();
65 dbprintf(("elks syscall returned %d\n", elks_cpu.regs.eax));
66 /* Finally return to vm86 state */
70 static int load_elks(int fd)
72 /* Load the elks binary image and set it up in a suitable VM86 segment. Load CS and DS/SS
73 according to image type. chmem is ignored we always use 64K segments */
74 struct elks_exec_hdr mh;
75 unsigned char *dsp;
76 if(read(fd, &mh,sizeof(mh))!=sizeof(mh))
77 return -ENOEXEC;
78 if(mh.hlen!=EXEC_HEADER_SIZE)
79 return -ENOEXEC;
80 if(mh.type!=ELKS_COMBID&&mh.type!=ELKS_SPLITID)
81 return -ENOEXEC;
82 #ifdef DEBUG
83 fprintf(stderr,"Linux-86 binary - %lX. tseg=%ld dseg=%ld bss=%ld\n",
84 mh.type,mh.tseg,mh.dseg,mh.bseg);
85 #endif
86 if(read(fd,elks_base,mh.tseg)!=mh.tseg)
87 return -ENOEXEC;
88 if(mh.type==ELKS_COMBID)
89 dsp=elks_base+mh.tseg;
90 else
91 dsp=elks_base+65536;
92 if(read(fd,dsp,mh.dseg)!=mh.dseg)
93 return -ENOEXEC;
94 memset(dsp+mh.dseg,0, mh.bseg);
96 * Load the VM86 registers
99 if(mh.type==ELKS_COMBID)
100 dsp=elks_base;
101 elks_cpu.regs.ds=PARAGRAPH(dsp);
102 elks_cpu.regs.es=PARAGRAPH(dsp);
103 elks_cpu.regs.ss=PARAGRAPH(dsp);
104 elks_cpu.regs.esp=65536; /* Args stacked later */
105 elks_cpu.regs.cs=PARAGRAPH(elks_base);
106 elks_cpu.regs.eip=0; /* Run from 0 */
109 * Loaded, check for sanity.
111 if( dsp != ELKS_PTR(unsigned char, 0) )
113 printf("Error VM86 problem %lx!=%lx (Is DS > 16 bits ?)\n",
114 (long)dsp, (long)ELKS_PTR(char, 0));
115 exit(0);
118 return 0;
121 #ifndef OLD_LIBC_VERSION
123 * recent versions of libc have changed the proto for vm86()
124 * for now I'll just override ...
126 #define OLD_SYS_vm86 113
127 #define NEW_SYS_vm86 166
129 static inline int vm86_mine(struct vm86_struct* v86)
131 int __res;
132 __asm__ __volatile__("int $0x80\n"
133 :"=a" (__res):"a" ((int)OLD_SYS_vm86), "b" ((int)v86));
134 return __res;
136 #endif
138 void run_elks()
141 * Execute 8086 code for a while.
143 #ifndef OLD_LIBC_VERSION
144 int err=vm86_mine((struct vm86_struct*)&elks_cpu);
145 #else
146 int err=vm86((struct vm86_struct*)&elks_cpu);
147 #endif
148 switch(VM86_TYPE(err))
151 * Signals are just re-starts of emulation (yes the
152 * handler might alter elks_cpu)
154 case VM86_SIGNAL:
155 break;
156 case VM86_UNKNOWN:
157 fprintf(stderr, "VM86_UNKNOWN returned\n");
158 exit(1);
159 case VM86_INTx:
160 elks_take_interrupt(VM86_ARG(err));
161 break;
162 case VM86_STI:
163 fprintf(stderr, "VM86_STI returned\n");
164 break; /* Shouldnt be seen */
168 void build_stack(char ** argv, char ** envp)
170 char **p;
171 int argv_len=0, argv_count=0;
172 int envp_len=0, envp_count=0;
173 int stack_bytes;
174 unsigned short * pip;
175 unsigned short pcp;
177 /* How much space for argv */
178 for(p=argv; *p; p++)
180 argv_count++; argv_len += strlen(*p)+1;
183 /* How much space for envp */
184 for(p=envp; *p; p++)
186 envp_count++; envp_len += strlen(*p)+1;
189 /* tot it all up */
190 stack_bytes = 2 /* argc */
191 + argv_count * 2 + 2 /* argv */
192 + argv_len
193 + envp_count * 2 + 2 /* envp */
194 + envp_len;
196 /* Allocate it */
197 elks_cpu.regs.esp -= stack_bytes;
199 /* Sanity check
200 printf("Argv = (%d,%d), Envp=(%d,%d), stack=%d\n",
201 argv_count, argv_len, envp_count, envp_len, stack_bytes);
204 /* Now copy in the strings */
205 pip=ELKS_PTR(unsigned short, elks_cpu.regs.esp);
206 pcp=elks_cpu.regs.esp+2*(1+argv_count+1+envp_count+1);
208 *pip++ = argv_count;
209 for(p=argv; *p; p++)
211 *pip++ = pcp;
212 strcpy(ELKS_PTR(char, pcp), *p);
213 pcp += strlen(*p)+1;
215 *pip++ = 0;
217 for(p=envp; *p; p++)
219 *pip++ = pcp;
220 strcpy(ELKS_PTR(char, pcp), *p);
221 pcp += strlen(*p)+1;
223 *pip++ = 0;
227 main(int argc, char *argv[], char *envp[])
229 int fd;
230 struct stat st;
231 int ruid, euid, rgid, egid;
233 if(argc<=1)
235 fprintf(stderr,"elksemu cmd args.....\n");
236 exit(1);
238 /* This uses the _real_ user ID If the file is exec only that's */
239 /* ok cause the suid root will override. */
240 /* BTW, be careful here, security problems are possible because of
241 * races if you change this. */
243 if( access(argv[1], X_OK) < 0
244 || (fd=open(argv[1], O_RDONLY)) < 0
245 || fstat(fd, &st) < 0
248 perror(argv[1]);
249 exit(1);
252 /* Check the suid bits ... */
253 ruid = getuid(); rgid = getgid();
254 euid = ruid; egid = rgid;
255 if( st.st_mode & S_ISUID ) euid = st.st_uid;
256 if( st.st_mode & S_ISGID ) egid = st.st_gid;
258 /* Set the _real_ permissions, or revoke superuser priviliages */
259 setregid(rgid, egid);
260 setreuid(ruid, euid);
262 dbprintf(("ELKSEMU\n"));
263 elks_init();
265 /* The Linux vm will deal with not allocating the unused pages */
266 #if __AOUT__
267 #if __GNUC__
268 /* GNU malloc will align to 4k with large chunks */
269 elks_base = malloc(0x20000);
270 #else
271 /* But others won't */
272 elks_base = malloc(0x20000+4096);
273 elks_base = (void*) (((int)elks_base+4095) & -4096);
274 #endif
275 #else
276 /* For ELF first 128M is unmapped, it needs to be mapped manually */
277 elks_base = mmap((void*)0x10000, 0x20000,
278 PROT_EXEC|PROT_READ|PROT_WRITE,
279 MAP_ANON|MAP_PRIVATE|MAP_FIXED,
280 0, 0);
281 #endif
282 if( (long)elks_base < 0 || (long)elks_base >= 0xE0000 )
284 fprintf(stderr, "Elks memory is at an illegal address\n");
285 exit(255);
288 if(load_elks(fd) < 0)
290 fprintf(stderr,"Not a elks binary.\n");
291 exit(1);
294 close(fd);
296 build_stack(argv+1, envp);
298 while(1)
299 run_elks();
302 #ifdef DEBUG
303 void db_printf(const char * fmt, ...)
305 static FILE * db_fd = 0;
306 va_list ptr;
307 int rv;
308 if( db_fd == 0 )
310 db_fd = fopen("/tmp/ELKS_log", "a");
311 if( db_fd == 0 ) db_fd = stderr;
312 setbuf(db_fd, 0);
314 fprintf(db_fd, "%d: ", getpid());
315 va_start(ptr, fmt);
316 rv = vfprintf(db_fd,fmt,ptr);
317 va_end(ptr);
319 #endif