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