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