Release 950202
[wine] / loader / signal.c
1 #ifndef WINELIB
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <signal.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <time.h>
8 #include <setjmp.h>
9
10 #if defined(__NetBSD__) || defined(__FreeBSD__)
11 #include <sys/syscall.h>
12 #include <sys/param.h>
13 #else
14 #include <syscall.h>
15 #endif
16
17 #include "wine.h"
18 #include "dos_fs.h"
19 #include "segmem.h"
20 #include "prototypes.h"
21 #include "miscemu.h"
22 #include "win.h"
23
24 #if !defined(BSD4_4) || defined(linux) || defined(__FreeBSD__)
25 char * cstack[4096];
26 #endif
27 struct sigaction segv_act;
28
29 #ifdef linux
30 extern void ___sig_restore();
31 extern void ___masksig_restore();
32
33 /* Similar to the sigaction function in libc, except it leaves alone the
34    restorer field */
35
36 static int
37 wine_sigaction(int sig,struct sigaction * new, struct sigaction * old)
38 {
39         __asm__("int $0x80":"=a" (sig)
40                 :"0" (SYS_sigaction),"b" (sig),"c" (new),"d" (old));
41         if (sig>=0)
42                 return 0;
43         errno = -sig;
44         return -1;
45 }
46 #endif
47
48 int do_int(int intnum, struct sigcontext_struct *scp)
49 {
50         switch(intnum)
51         {
52               case 0x10: return do_int10(scp);
53
54               case 0x11:  
55                 scp->sc_eax = (scp->sc_eax & 0xffff0000L) | DOS_GetEquipment();
56                 return 1;
57
58               case 0x12:               
59                 scp->sc_eax = (scp->sc_eax & 0xffff0000L) | 640L; 
60                 return 1;       /* get base mem size */                
61
62               case 0x13: return do_int13(scp);
63               case 0x15: return do_int15(scp);
64               case 0x16: return do_int16(scp);
65               case 0x1a: return do_int1a(scp);
66               case 0x21: return do_int21(scp);
67
68               case 0x22:
69                 scp->sc_eax = 0x1234;
70                 scp->sc_ebx = 0x5678;
71                 scp->sc_ecx = 0x9abc;
72                 scp->sc_edx = 0xdef0;
73                 return 1;
74
75               case 0x25: return do_int25(scp);
76               case 0x26: return do_int26(scp);
77               case 0x2a: return do_int2a(scp);
78               case 0x2f: return do_int2f(scp);
79               case 0x31: return do_int31(scp);
80         }
81         return 0;
82 }
83
84 #ifdef linux
85 static void win_fault(int signal, struct sigcontext_struct context)
86 {
87     struct sigcontext_struct *scp = &context;
88 #else
89 static void win_fault(int signal, int code, struct sigcontext *scp)
90 {
91 #endif
92     unsigned char * instr;
93 #if !(defined (linux) || defined (__NetBSD__))
94         int i, *dump;
95 #endif
96
97         /* First take care of a few preliminaries */
98 #ifdef linux
99     if(signal != SIGSEGV 
100        && signal != SIGILL 
101 #ifdef SIGBUS
102        && signal != SIGBUS 
103 #endif
104        && signal != SIGTRAP) 
105     {
106         exit(1);
107     }
108
109     /* And back up over the int3 instruction. */
110     if(signal == SIGTRAP) {
111       scp->sc_eip--;
112       goto oops;
113     };
114
115     if((scp->sc_cs & 7) != 7)
116     {
117 #endif
118 #if defined(__NetBSD__) || defined(__FreeBSD__)
119 /*         set_es(0x27); set_ds(0x27); */
120     if(signal != SIGBUS && signal != SIGSEGV && signal != SIGTRAP) 
121         exit(1);
122     if(scp->sc_cs == 0x1f)
123     {
124 #endif
125         fprintf(stderr,
126                 "Segmentation fault in Wine program (%x:%lx)."
127                 "  Please debug\n",
128                 scp->sc_cs, scp->sc_eip);
129         goto oops;
130     };
131
132     /*  Now take a look at the actual instruction where the program
133         bombed */
134     instr = (unsigned char *) SAFEMAKEPTR(scp->sc_cs, scp->sc_eip);
135
136     switch(*instr)
137     {
138       case 0xcd: /* int <XX> */
139             instr++;
140             if (!do_int(*instr, scp)) {
141                 fprintf(stderr,"Unexpected Windows interrupt %x\n", *instr);
142                 goto oops;
143             }
144             scp->sc_eip += 2;  /* Bypass the int instruction */
145             break;
146             
147       case 0xe4: /* inb al,XX */
148             inportb_abs(scp);
149             scp->sc_eip += 2;
150             break;
151
152       case 0xe5: /* in ax,XX */
153             inport_abs(scp);
154             scp->sc_eip += 2;
155             break;
156
157       case 0xe6: /* outb XX,al */
158             outportb_abs(scp);
159             scp->sc_eip += 2;
160             break;
161
162       case 0xe7: /* out XX,ax */
163             outport_abs(scp);
164             scp->sc_eip += 2;
165             break;
166
167       case 0xec: /* inb al,dx */
168             inportb(scp);
169             scp->sc_eip++;
170             break;
171
172       case 0xed: /* in ax,dx */
173             inport(scp);
174             scp->sc_eip++;  
175             break;
176
177       case 0xee: /* outb dx,al */
178             outportb(scp);
179             scp->sc_eip++;
180             break;
181       
182       case 0xef: /* out dx,ax */
183             outport(scp);
184             scp->sc_eip++;
185             break;
186
187       case 0xfa: /* cli, ignored */
188             scp->sc_eip++;
189             break;
190
191       case 0xfb: /* sti, ignored */
192             scp->sc_eip++;
193             break;
194
195       default:
196                 fprintf(stderr, "Unexpected Windows program segfault"
197                         " - opcode = %x\n", *instr);
198                 goto oops;
199     }
200     
201     /* OK, done handling the interrupt */
202
203     return;
204
205   oops:
206     XUngrabPointer(display, CurrentTime);
207         XUngrabServer(display);
208         XFlush(display);
209     fprintf(stderr,"In win_fault %x:%lx\n", scp->sc_cs, scp->sc_eip);
210 #if defined(linux) || defined(__NetBSD__) || defined(__FreeBSD__)
211     wine_debug(signal, (int *)scp);  /* Enter our debugger */
212 #else
213     fprintf(stderr,"Stack: %x:%x\n", scp->sc_ss, scp->sc_esp);
214     dump = (int*) scp;
215     for(i=0; i<22; i++) 
216     {
217         fprintf(stderr," %8.8x", *dump++);
218         if ((i % 8) == 7)
219             fprintf(stderr,"\n");
220     }
221     fprintf(stderr,"\n");
222     exit(1);
223 #endif
224 }
225
226 void init_wine_signals(void)
227 {
228 #ifdef linux
229         segv_act.sa_handler = (__sighandler_t) win_fault;
230         /* Point to the top of the stack, minus 4 just in case, and make
231            it aligned  */
232         segv_act.sa_restorer = 
233                 (void (*)()) (((unsigned int)(cstack) + sizeof(cstack) - 4) & ~3);
234         wine_sigaction(SIGSEGV, &segv_act, NULL);
235         wine_sigaction(SIGILL, &segv_act, NULL);
236 #ifdef SIGBUS
237         wine_sigaction(SIGBUS, &segv_act, NULL);
238 #endif
239         wine_sigaction(SIGTRAP, &segv_act, NULL); /* For breakpoints */
240 #endif
241 #if defined(__NetBSD__) || defined(__FreeBSD__)
242         sigset_t sig_mask;
243 #if defined(BSD4_4) && !defined (__FreeBSD__)
244         struct sigaltstack ss;
245         
246         if ((ss.ss_base = malloc(MINSIGSTKSZ)) == NULL) {
247                 fprintf(stderr, "Unable to allocate signal stack (%d bytes)\n",
248                         MINSIGSTKSZ);
249                 exit(1);
250         }
251         ss.ss_size = MINSIGSTKSZ;
252         ss.ss_flags = 0;
253         if (sigaltstack(&ss, NULL) < 0) {
254                 perror("sigstack");
255                 exit(1);
256         }
257 #else
258         struct sigstack ss;
259         
260         ss.ss_sp = (char *) (((unsigned int)(cstack) + sizeof(cstack) - 4) & ~3);
261         ss.ss_onstack = 0;
262         if (sigstack(&ss, NULL) < 0) {
263                 perror("sigstack");
264                 exit(1);
265         }
266 #endif
267         sigemptyset(&sig_mask);
268         segv_act.sa_handler = (void (*)) win_fault;
269         segv_act.sa_flags = SA_ONSTACK;
270         segv_act.sa_mask = sig_mask;
271         if (sigaction(SIGBUS, &segv_act, NULL) < 0) {
272                 perror("sigaction: SIGBUS");
273                 exit(1);
274         }
275         segv_act.sa_handler = (void (*)) win_fault;
276         segv_act.sa_flags = SA_ONSTACK;
277         segv_act.sa_mask = sig_mask;
278         if (sigaction(SIGSEGV, &segv_act, NULL) < 0) {
279                 perror("sigaction: SIGSEGV");
280                 exit(1);
281         }
282         segv_act.sa_handler = (void (*)) win_fault; /* For breakpoints */
283         segv_act.sa_flags = SA_ONSTACK;
284         segv_act.sa_mask = sig_mask;
285         if (sigaction(SIGTRAP, &segv_act, NULL) < 0) {
286                 perror("sigaction: SIGTRAP");
287                 exit(1);
288         }
289 #endif
290 }
291
292 static sigjmp_buf segv_jmpbuf;
293
294 static void
295 segv_handler()
296 {
297     siglongjmp(segv_jmpbuf, 1);
298 }
299
300 int
301 test_memory( char *p, int write )
302 {
303     int ret = FALSE;
304     struct sigaction new_act;
305     struct sigaction old_act;
306
307     memset(&new_act, 0, sizeof new_act);
308     new_act.sa_handler = segv_handler;
309     if (sigsetjmp( segv_jmpbuf, 1 ) == 0) {
310         char c = 100;
311         if (sigaction(SIGSEGV, &new_act, &old_act) < 0)
312             perror("sigaction");
313         c = *p;
314         if (write)
315             *p = c;
316         ret = TRUE;
317     }
318 #ifdef linux
319     wine_sigaction(SIGSEGV, &old_act, NULL);
320 #else
321     sigaction(SIGSEGV, &old_act, NULL);
322 #endif
323     return ret;
324 }
325
326 #endif /* ifndef WINELIB */