Rename __WINE__ to __WINESRC__.
[wine] / server / context_i386.c
1 /*
2  * i386 register context support
3  *
4  * Copyright (C) 1999 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #ifdef __i386__
24
25 #include <assert.h>
26 #include <errno.h>
27 #ifdef HAVE_SYS_REG_H
28 #include <sys/reg.h>
29 #endif
30 #include <unistd.h>
31 #ifdef HAVE_SYS_PTRACE_H
32 # include <sys/ptrace.h>
33 #endif
34 #ifdef HAVE_SYS_PARAM_H
35 # include <sys/param.h>
36 #endif
37
38 #include "winbase.h"
39 #include "thread.h"
40 #include "request.h"
41
42 #ifndef PTRACE_PEEKUSER
43 #  ifdef PTRACE_PEEKUSR /* libc5 uses USR not USER */
44 #    define PTRACE_PEEKUSER PTRACE_PEEKUSR
45 #  else
46 #    define PTRACE_PEEKUSER PT_READ_U
47 #  endif
48 #endif
49
50 #ifndef PTRACE_POKEUSER
51 #  ifdef PTRACE_POKEUSR /* libc5 uses USR not USER */
52 #    define PTRACE_POKEUSER PTRACE_POKEUSR
53 #  else
54 #    define PTRACE_POKEUSER PT_WRITE_U
55 #  endif
56 #endif
57
58 #ifndef PTRACE_GETREGS
59 #define PTRACE_GETREGS PT_GETREGS
60 #endif
61 #ifndef PTRACE_GETFPREGS
62 #define PTRACE_GETFPREGS PT_GETFPREGS
63 #endif
64 #ifndef PTRACE_SETREGS
65 #define PTRACE_SETREGS PT_SETREGS
66 #endif
67 #ifndef PTRACE_SETFPREGS
68 #define PTRACE_SETFPREGS PT_SETFPREGS
69 #endif
70
71 #ifdef PT_GETDBREGS
72 #define PTRACE_GETDBREGS PT_GETDBREGS
73 #endif
74
75 #ifdef PT_SETDBREGS
76 #define PTRACE_SETDBREGS PT_SETDBREGS
77 #endif
78
79 #ifdef linux
80 #ifdef HAVE_SYS_USER_H
81 # include <sys/user.h>
82 #endif
83
84 /* user_regs definitions from asm/user.h */
85 struct kernel_user_regs_struct
86 {
87     long ebx, ecx, edx, esi, edi, ebp, eax;
88     unsigned short ds, __ds, es, __es;
89     unsigned short fs, __fs, gs, __gs;
90     long orig_eax, eip;
91     unsigned short cs, __cs;
92     long eflags, esp;
93     unsigned short ss, __ss;
94 };
95
96 /* debug register offset in struct user */
97 #define DR_OFFSET(dr) ((int)((((struct user *)0)->u_debugreg) + (dr)))
98
99 /* retrieve a debug register */
100 static inline int get_debug_reg( int pid, int num, DWORD *data )
101 {
102     int res = ptrace( PTRACE_PEEKUSER, pid, DR_OFFSET(num), 0 );
103     if ((res == -1) && errno)
104     {
105         file_set_error();
106         return -1;
107     }
108     *data = res;
109     return 0;
110 }
111
112 /* retrieve a thread context */
113 static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
114 {
115     int pid = thread->unix_pid;
116     if (flags & CONTEXT_FULL)
117     {
118         struct kernel_user_regs_struct regs;
119         if (ptrace( PTRACE_GETREGS, pid, 0, &regs ) == -1) goto error;
120         if (flags & CONTEXT_INTEGER)
121         {
122             context->Eax = regs.eax;
123             context->Ebx = regs.ebx;
124             context->Ecx = regs.ecx;
125             context->Edx = regs.edx;
126             context->Esi = regs.esi;
127             context->Edi = regs.edi;
128         }
129         if (flags & CONTEXT_CONTROL)
130         {
131             context->Ebp    = regs.ebp;
132             context->Esp    = regs.esp;
133             context->Eip    = regs.eip;
134             context->SegCs  = regs.cs;
135             context->SegSs  = regs.ss;
136             context->EFlags = regs.eflags;
137         }
138         if (flags & CONTEXT_SEGMENTS)
139         {
140             context->SegDs = regs.ds;
141             context->SegEs = regs.es;
142             context->SegFs = regs.fs;
143             context->SegGs = regs.gs;
144         }
145     }
146     if (flags & CONTEXT_DEBUG_REGISTERS)
147     {
148         if (get_debug_reg( pid, 0, &context->Dr0 ) == -1) goto error;
149         if (get_debug_reg( pid, 1, &context->Dr1 ) == -1) goto error;
150         if (get_debug_reg( pid, 2, &context->Dr2 ) == -1) goto error;
151         if (get_debug_reg( pid, 3, &context->Dr3 ) == -1) goto error;
152         if (get_debug_reg( pid, 6, &context->Dr6 ) == -1) goto error;
153         if (get_debug_reg( pid, 7, &context->Dr7 ) == -1) goto error;
154     }
155     if (flags & CONTEXT_FLOATING_POINT)
156     {
157         /* we can use context->FloatSave directly as it is using the */
158         /* correct structure (the same as fsave/frstor) */
159         if (ptrace( PTRACE_GETFPREGS, pid, 0, &context->FloatSave ) == -1) goto error;
160         context->FloatSave.Cr0NpxState = 0;  /* FIXME */
161     }
162     return;
163  error:
164     file_set_error();
165 }
166
167
168 /* set a thread context */
169 static void set_thread_context( struct thread *thread, unsigned int flags, const CONTEXT *context )
170 {
171     int pid = thread->unix_pid;
172     if (flags & CONTEXT_FULL)
173     {
174         struct kernel_user_regs_struct regs;
175
176         /* need to preserve some registers (at a minimum orig_eax must always be preserved) */
177         if (ptrace( PTRACE_GETREGS, pid, 0, &regs ) == -1) goto error;
178
179         if (flags & CONTEXT_INTEGER)
180         {
181             regs.eax = context->Eax;
182             regs.ebx = context->Ebx;
183             regs.ecx = context->Ecx;
184             regs.edx = context->Edx;
185             regs.esi = context->Esi;
186             regs.edi = context->Edi;
187         }
188         if (flags & CONTEXT_CONTROL)
189         {
190             regs.ebp = context->Ebp;
191             regs.esp = context->Esp;
192             regs.eip = context->Eip;
193             regs.cs  = context->SegCs;
194             regs.ss  = context->SegSs;
195             regs.eflags = context->EFlags;
196         }
197         if (flags & CONTEXT_SEGMENTS)
198         {
199             regs.ds = context->SegDs;
200             regs.es = context->SegEs;
201             regs.fs = context->SegFs;
202             regs.gs = context->SegGs;
203         }
204         if (ptrace( PTRACE_SETREGS, pid, 0, &regs ) == -1) goto error;
205     }
206     if (flags & CONTEXT_DEBUG_REGISTERS)
207     {
208         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(0), context->Dr0 ) == -1) goto error;
209         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(1), context->Dr1 ) == -1) goto error;
210         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(2), context->Dr2 ) == -1) goto error;
211         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(3), context->Dr3 ) == -1) goto error;
212         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(6), context->Dr6 ) == -1) goto error;
213         if (ptrace( PTRACE_POKEUSER, pid, DR_OFFSET(7), context->Dr7 ) == -1) goto error;
214     }
215     if (flags & CONTEXT_FLOATING_POINT)
216     {
217         /* we can use context->FloatSave directly as it is using the */
218         /* correct structure (the same as fsave/frstor) */
219         if (ptrace( PTRACE_SETFPREGS, pid, 0, &context->FloatSave ) == -1) goto error;
220     }
221     return;
222  error:
223     file_set_error();
224 }
225
226 #elif defined(__sun) || defined(__sun__)
227
228 /* retrieve a thread context */
229 static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
230 {
231     int pid = thread->unix_pid;
232     if (flags & CONTEXT_FULL)
233     {
234         struct regs regs;
235         if (ptrace( PTRACE_GETREGS, pid, (int) &regs, 0 ) == -1) goto error;
236         if (flags & CONTEXT_INTEGER)
237         {
238             context->Eax = regs.r_eax;
239             context->Ebx = regs.r_ebx;
240             context->Ecx = regs.r_ecx;
241             context->Edx = regs.r_edx;
242             context->Esi = regs.r_esi;
243             context->Edi = regs.r_edi;
244         }
245         if (flags & CONTEXT_CONTROL)
246         {
247             context->Ebp    = regs.r_ebp;
248             context->Esp    = regs.r_esp;
249             context->Eip    = regs.r_eip;
250             context->SegCs  = regs.r_cs & 0xffff;
251             context->SegSs  = regs.r_ss & 0xffff;
252             context->EFlags = regs.r_efl;
253         }
254         if (flags & CONTEXT_SEGMENTS)
255         {
256             context->SegDs = regs.r_ds & 0xffff;
257             context->SegEs = regs.r_es & 0xffff;
258             context->SegFs = regs.r_fs & 0xffff;
259             context->SegGs = regs.r_gs & 0xffff;
260         }
261     }
262     if (flags & CONTEXT_DEBUG_REGISTERS)
263     {
264         /* FIXME: How is this done on Solaris? */
265     }
266     if (flags & CONTEXT_FLOATING_POINT)
267     {
268         /* we can use context->FloatSave directly as it is using the */
269         /* correct structure (the same as fsave/frstor) */
270         if (ptrace( PTRACE_GETFPREGS, pid, (int) &context->FloatSave, 0 ) == -1) goto error;
271         context->FloatSave.Cr0NpxState = 0;  /* FIXME */
272     }
273     return;
274  error:
275     file_set_error();
276 }
277
278
279 /* set a thread context */
280 static void set_thread_context( struct thread *thread, unsigned int flags, const CONTEXT *context )
281 {
282     int pid = thread->unix_pid;
283     if (flags & CONTEXT_FULL)
284     {
285         struct regs regs;
286         if (((flags | CONTEXT_i386) & CONTEXT_FULL) != CONTEXT_FULL)
287         {
288             /* need to preserve some registers */
289             if (ptrace( PTRACE_GETREGS, pid, (int) &regs, 0 ) == -1) goto error;
290         }
291         if (flags & CONTEXT_INTEGER)
292         {
293             regs.r_eax = context->Eax;
294             regs.r_ebx = context->Ebx;
295             regs.r_ecx = context->Ecx;
296             regs.r_edx = context->Edx;
297             regs.r_esi = context->Esi;
298             regs.r_edi = context->Edi;
299         }
300         if (flags & CONTEXT_CONTROL)
301         {
302             regs.r_ebp = context->Ebp;
303             regs.r_esp = context->Esp;
304             regs.r_eip = context->Eip;
305             regs.r_cs = context->SegCs;
306             regs.r_ss = context->SegSs;
307             regs.r_efl = context->EFlags;
308         }
309         if (flags & CONTEXT_SEGMENTS)
310         {
311             regs.r_ds = context->SegDs;
312             regs.r_es = context->SegEs;
313             regs.r_fs = context->SegFs;
314             regs.r_gs = context->SegGs;
315         }
316         if (ptrace( PTRACE_SETREGS, pid, (int) &regs, 0 ) == -1) goto error;
317     }
318     if (flags & CONTEXT_DEBUG_REGISTERS)
319     {
320         /* FIXME: How is this done on Solaris? */
321     }
322     if (flags & CONTEXT_FLOATING_POINT)
323     {
324         /* we can use context->FloatSave directly as it is using the */
325         /* correct structure (the same as fsave/frstor) */
326         if (ptrace( PTRACE_SETFPREGS, pid, (int) &context->FloatSave, 0 ) == -1) goto error;
327     }
328     return;
329  error:
330     file_set_error();
331 }
332
333 #elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
334 #include <machine/reg.h>
335
336 /* retrieve a thread context */
337 static void get_thread_context( struct thread *thread, unsigned int flags, CONTEXT *context )
338 {
339     int pid = thread->unix_pid;
340     if (flags & CONTEXT_FULL)
341     {
342         struct reg regs;
343         if (ptrace( PTRACE_GETREGS, pid, (caddr_t) &regs, 0 ) == -1) goto error;
344         if (flags & CONTEXT_INTEGER)
345         {
346             context->Eax = regs.r_eax;
347             context->Ebx = regs.r_ebx;
348             context->Ecx = regs.r_ecx;
349             context->Edx = regs.r_edx;
350             context->Esi = regs.r_esi;
351             context->Edi = regs.r_edi;
352         }
353         if (flags & CONTEXT_CONTROL)
354         {
355             context->Ebp    = regs.r_ebp;
356             context->Esp    = regs.r_esp;
357             context->Eip    = regs.r_eip;
358             context->SegCs  = regs.r_cs & 0xffff;
359             context->SegSs  = regs.r_ss & 0xffff;
360             context->EFlags = regs.r_eflags;
361         }
362         if (flags & CONTEXT_SEGMENTS)
363         {
364             context->SegDs = regs.r_ds & 0xffff;
365             context->SegEs = regs.r_es & 0xffff;
366             context->SegFs = regs.r_fs & 0xffff;
367             context->SegGs = regs.r_gs & 0xffff;
368         }
369     }
370     if (flags & CONTEXT_DEBUG_REGISTERS)
371     {
372 #ifdef PTRACE_GETDBREGS
373         struct dbreg dbregs;
374         if (ptrace( PTRACE_GETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1)
375                 goto error;
376 #ifdef DBREG_DRX
377         /* needed for FreeBSD, the structure fields have changed under 5.x */
378         context->Dr0 = DBREG_DRX((&dbregs), 0);
379         context->Dr1 = DBREG_DRX((&dbregs), 1);
380         context->Dr2 = DBREG_DRX((&dbregs), 2);
381         context->Dr3 = DBREG_DRX((&dbregs), 3);
382         context->Dr6 = DBREG_DRX((&dbregs), 6);
383         context->Dr7 = DBREG_DRX((&dbregs), 7);
384 #else
385         context->Dr0 = dbregs.dr0;
386         context->Dr1 = dbregs.dr1;
387         context->Dr2 = dbregs.dr2;
388         context->Dr3 = dbregs.dr3;
389         context->Dr6 = dbregs.dr6;
390         context->Dr7 = dbregs.dr7;
391 #endif
392
393 #endif
394     }
395     if (flags & CONTEXT_FLOATING_POINT)
396     {
397         /* we can use context->FloatSave directly as it is using the */
398         /* correct structure (the same as fsave/frstor) */
399         if (ptrace( PTRACE_GETFPREGS, pid, (caddr_t) &context->FloatSave, 0 ) == -1) goto error;
400         context->FloatSave.Cr0NpxState = 0;  /* FIXME */
401     }
402     return;
403  error:
404     file_set_error();
405 }
406
407
408 /* set a thread context */
409 static void set_thread_context( struct thread *thread, unsigned int flags, const CONTEXT *context )
410 {
411     int pid = thread->unix_pid;
412     if (flags & CONTEXT_FULL)
413     {
414         struct reg regs;
415         if (((flags | CONTEXT_i386) & CONTEXT_FULL) != CONTEXT_FULL)
416         {
417             /* need to preserve some registers */
418             if (ptrace( PTRACE_GETREGS, pid, (caddr_t) &regs, 0 ) == -1) goto error;
419         }
420         if (flags & CONTEXT_INTEGER)
421         {
422             regs.r_eax = context->Eax;
423             regs.r_ebx = context->Ebx;
424             regs.r_ecx = context->Ecx;
425             regs.r_edx = context->Edx;
426             regs.r_esi = context->Esi;
427             regs.r_edi = context->Edi;
428         }
429         if (flags & CONTEXT_CONTROL)
430         {
431             regs.r_ebp = context->Ebp;
432             regs.r_esp = context->Esp;
433             regs.r_eip = context->Eip;
434             regs.r_cs = context->SegCs;
435             regs.r_ss = context->SegSs;
436             regs.r_eflags = context->EFlags;
437         }
438         if (flags & CONTEXT_SEGMENTS)
439         {
440             regs.r_ds = context->SegDs;
441             regs.r_es = context->SegEs;
442             regs.r_fs = context->SegFs;
443             regs.r_gs = context->SegGs;
444         }
445         if (ptrace( PTRACE_SETREGS, pid, (caddr_t) &regs, 0 ) == -1) goto error;
446     }
447     if (flags & CONTEXT_DEBUG_REGISTERS)
448     {
449 #ifdef PTRACE_SETDBREGS
450         struct dbreg dbregs;
451 #ifdef DBREG_DRX
452         /* needed for FreeBSD, the structure fields have changed under 5.x */
453         DBREG_DRX((&dbregs), 0) = context->Dr0;
454         DBREG_DRX((&dbregs), 1) = context->Dr1;
455         DBREG_DRX((&dbregs), 2) = context->Dr2;
456         DBREG_DRX((&dbregs), 3) = context->Dr3;
457         DBREG_DRX((&dbregs), 4) = 0;
458         DBREG_DRX((&dbregs), 5) = 0;
459         DBREG_DRX((&dbregs), 6) = context->Dr6;
460         DBREG_DRX((&dbregs), 7) = context->Dr7;
461 #else
462         dbregs.dr0 = context->Dr0;
463         dbregs.dr1 = context->Dr1;
464         dbregs.dr2 = context->Dr2;
465         dbregs.dr3 = context->Dr3;
466         dbregs.dr4 = 0;
467         dbregs.dr5 = 0;
468         dbregs.dr6 = context->Dr6;
469         dbregs.dr7 = context->Dr7;
470 #endif
471         if (ptrace( PTRACE_SETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1)
472                 goto error;
473 #endif
474     }
475     if (flags & CONTEXT_FLOATING_POINT)
476     {
477         /* we can use context->FloatSave directly as it is using the */
478         /* correct structure (the same as fsave/frstor) */
479         if (ptrace( PTRACE_SETFPREGS, pid, (caddr_t) &context->FloatSave, 0 ) == -1) goto error;
480     }
481     return;
482  error:
483     file_set_error();
484 }
485
486 #else  /* linux || __sun__ || __FreeBSD__ */
487 #error You must implement get/set_thread_context for your platform
488 #endif  /* linux || __sun__ || __FreeBSD__ */
489
490
491 /* copy a context structure according to the flags */
492 static void copy_context( CONTEXT *to, const CONTEXT *from, int flags )
493 {
494     if (flags & CONTEXT_CONTROL)
495     {
496         to->Ebp    = from->Ebp;
497         to->Eip    = from->Eip;
498         to->Esp    = from->Esp;
499         to->SegCs  = from->SegCs;
500         to->SegSs  = from->SegSs;
501         to->EFlags = from->EFlags;
502     }
503     if (flags & CONTEXT_INTEGER)
504     {
505         to->Eax = from->Eax;
506         to->Ebx = from->Ebx;
507         to->Ecx = from->Ecx;
508         to->Edx = from->Edx;
509         to->Esi = from->Esi;
510         to->Edi = from->Edi;
511     }
512     if (flags & CONTEXT_SEGMENTS)
513     {
514         to->SegDs = from->SegDs;
515         to->SegEs = from->SegEs;
516         to->SegFs = from->SegFs;
517         to->SegGs = from->SegGs;
518     }
519     if (flags & CONTEXT_FLOATING_POINT)
520     {
521         to->FloatSave = from->FloatSave;
522     }
523     /* we don't bother copying the debug registers, since they */
524     /* always need to be accessed by ptrace anyway */
525 }
526
527 /* retrieve the current instruction pointer of a thread */
528 void *get_thread_ip( struct thread *thread )
529 {
530     CONTEXT context;
531     context.Eip = 0;
532     if (suspend_for_ptrace( thread ))
533     {
534         get_thread_context( thread, CONTEXT_CONTROL, &context );
535         resume_thread( thread );
536     }
537     return (void *)context.Eip;
538 }
539
540 /* determine if we should continue the thread in single-step mode */
541 int get_thread_single_step( struct thread *thread )
542 {
543     CONTEXT context;
544     if (thread->context) return 0;  /* don't single-step inside exception event */
545     get_thread_context( thread, CONTEXT_CONTROL, &context );
546     return (context.EFlags & 0x100) != 0;
547 }
548
549 /* retrieve the current context of a thread */
550 DECL_HANDLER(get_thread_context)
551 {
552     struct thread *thread;
553     void *data;
554     int flags = req->flags & ~CONTEXT_i386;  /* get rid of CPU id */
555
556     if (get_reply_max_size() < sizeof(CONTEXT))
557     {
558         set_error( STATUS_INVALID_PARAMETER );
559         return;
560     }
561     if (!(thread = get_thread_from_handle( req->handle, THREAD_GET_CONTEXT ))) return;
562
563     if ((data = set_reply_data_size( sizeof(CONTEXT) )))
564     {
565         /* copy incoming context into reply */
566         memset( data, 0, sizeof(CONTEXT) );
567         memcpy( data, get_req_data(), min( get_req_data_size(), sizeof(CONTEXT) ));
568
569         if (thread->context)  /* thread is inside an exception event */
570         {
571             copy_context( data, thread->context, flags );
572             flags &= CONTEXT_DEBUG_REGISTERS;
573         }
574         if (flags && suspend_for_ptrace( thread ))
575         {
576             get_thread_context( thread, flags, data );
577             resume_thread( thread );
578         }
579     }
580     release_object( thread );
581 }
582
583
584 /* set the current context of a thread */
585 DECL_HANDLER(set_thread_context)
586 {
587     struct thread *thread;
588     int flags = req->flags & ~CONTEXT_i386;  /* get rid of CPU id */
589
590     if (get_req_data_size() < sizeof(CONTEXT))
591     {
592         set_error( STATUS_INVALID_PARAMETER );
593         return;
594     }
595     if ((thread = get_thread_from_handle( req->handle, THREAD_SET_CONTEXT )))
596     {
597         if (thread->context)  /* thread is inside an exception event */
598         {
599             copy_context( thread->context, get_req_data(), flags );
600             flags &= CONTEXT_DEBUG_REGISTERS;
601         }
602         if (flags && suspend_for_ptrace( thread ))
603         {
604             set_thread_context( thread, flags, get_req_data() );
605             resume_thread( thread );
606         }
607         release_object( thread );
608     }
609 }
610
611 #endif  /* __i386__ */