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