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