]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/i386/linux/linux_ptrace.c
This commit was generated by cvs2svn to compensate for changes in r98121,
[FreeBSD/FreeBSD.git] / sys / i386 / linux / linux_ptrace.c
1 /*
2  * Copyright (c) 2001 Alexander Kabaev
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/lock.h>
34 #include <sys/mutex.h>
35 #include <sys/proc.h>
36 #include <sys/ptrace.h>
37 #include <sys/sysproto.h>
38 #include <sys/user.h>
39
40 #include <machine/md_var.h>
41 #include <machine/npx.h>
42 #include <machine/reg.h>
43
44 #include <i386/linux/linux.h>
45 #include <i386/linux/linux_proto.h>
46 #include <compat/linux/linux_util.h>
47
48 /*
49  *   Linux ptrace requests numbers. Mostly identical to FreeBSD,
50  *   except for MD ones and PT_ATTACH/PT_DETACH.
51  */
52 #define PTRACE_TRACEME          0
53 #define PTRACE_PEEKTEXT         1
54 #define PTRACE_PEEKDATA         2
55 #define PTRACE_PEEKUSR          3
56 #define PTRACE_POKETEXT         4
57 #define PTRACE_POKEDATA         5
58 #define PTRACE_POKEUSR          6
59 #define PTRACE_CONT             7
60 #define PTRACE_KILL             8
61 #define PTRACE_SINGLESTEP       9
62
63 #define PTRACE_ATTACH           16
64 #define PTRACE_DETACH           17
65
66 #define PTRACE_SYSCALL          24
67
68 #define PTRACE_GETREGS          12
69 #define PTRACE_SETREGS          13
70 #define PTRACE_GETFPREGS        14
71 #define PTRACE_SETFPREGS        15
72 #define PTRACE_GETFPXREGS       18
73 #define PTRACE_SETFPXREGS       19
74
75 #define PTRACE_SETOPTIONS       21
76
77 /*
78  * Linux keeps debug registers at the following
79  * offset in the user struct
80  */
81 #define LINUX_DBREG_OFFSET      252
82 #define LINUX_DBREG_SIZE        (8*sizeof(l_int))
83
84 static __inline__ int
85 map_signum(int signum)
86 {
87
88         if (signum > 0 && signum <= LINUX_SIGTBLSZ)
89                 signum = linux_to_bsd_signal[_SIG_IDX(signum)];
90         return ((signum == SIGSTOP)? 0 : signum);
91 }
92
93 struct linux_pt_reg {
94         l_long  ebx;
95         l_long  ecx;
96         l_long  edx;
97         l_long  esi;
98         l_long  edi;
99         l_long  ebp;
100         l_long  eax;
101         l_int   xds;
102         l_int   xes;
103         l_int   xfs;
104         l_int   xgs;
105         l_long  orig_eax;
106         l_long  eip;
107         l_int   xcs;
108         l_long  eflags;
109         l_long  esp;
110         l_int   xss;
111 };
112
113 /*
114  *   Translate i386 ptrace registers between Linux and FreeBSD formats.
115  *   The translation is pretty straighforward, for all registers, but
116  *   orig_eax on Linux side and r_trapno and r_err in FreeBSD
117  */
118 static void
119 map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)
120 {
121         linux_r->ebx = bsd_r->r_ebx;
122         linux_r->ecx = bsd_r->r_ecx;
123         linux_r->edx = bsd_r->r_edx;
124         linux_r->esi = bsd_r->r_esi;
125         linux_r->edi = bsd_r->r_edi;
126         linux_r->ebp = bsd_r->r_ebp;
127         linux_r->eax = bsd_r->r_eax;
128         linux_r->xds = bsd_r->r_ds;
129         linux_r->xes = bsd_r->r_es;
130         linux_r->xfs = bsd_r->r_fs;
131         linux_r->xgs = bsd_r->r_gs;
132         linux_r->orig_eax = bsd_r->r_eax;
133         linux_r->eip = bsd_r->r_eip;
134         linux_r->xcs = bsd_r->r_cs;
135         linux_r->eflags = bsd_r->r_eflags;
136         linux_r->esp = bsd_r->r_esp;
137         linux_r->xss = bsd_r->r_ss;
138 }
139
140 static void
141 map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)
142 {
143         bsd_r->r_ebx = linux_r->ebx;
144         bsd_r->r_ecx = linux_r->ecx;
145         bsd_r->r_edx = linux_r->edx;
146         bsd_r->r_esi = linux_r->esi;
147         bsd_r->r_edi = linux_r->edi;
148         bsd_r->r_ebp = linux_r->ebp;
149         bsd_r->r_eax = linux_r->eax;
150         bsd_r->r_ds  = linux_r->xds;
151         bsd_r->r_es  = linux_r->xes;
152         bsd_r->r_fs  = linux_r->xfs;
153         bsd_r->r_gs  = linux_r->xgs;
154         bsd_r->r_eip = linux_r->eip;
155         bsd_r->r_cs  = linux_r->xcs;
156         bsd_r->r_eflags = linux_r->eflags;
157         bsd_r->r_esp = linux_r->esp;
158         bsd_r->r_ss = linux_r->xss;
159 }
160
161 struct linux_pt_fpreg {
162         l_long cwd;
163         l_long swd;
164         l_long twd;
165         l_long fip;
166         l_long fcs;
167         l_long foo;
168         l_long fos;
169         l_long st_space[2*10];
170 };
171
172 static void
173 map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)
174 {
175         linux_r->cwd = bsd_r->fpr_env[0];
176         linux_r->swd = bsd_r->fpr_env[1];
177         linux_r->twd = bsd_r->fpr_env[2];
178         linux_r->fip = bsd_r->fpr_env[3];
179         linux_r->fcs = bsd_r->fpr_env[4];
180         linux_r->foo = bsd_r->fpr_env[5];
181         linux_r->fos = bsd_r->fpr_env[6];
182         bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space));
183 }
184
185 static void
186 map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)
187 {
188         bsd_r->fpr_env[0] = linux_r->cwd;
189         bsd_r->fpr_env[1] = linux_r->swd;
190         bsd_r->fpr_env[2] = linux_r->twd;
191         bsd_r->fpr_env[3] = linux_r->fip;
192         bsd_r->fpr_env[4] = linux_r->fcs;
193         bsd_r->fpr_env[5] = linux_r->foo;
194         bsd_r->fpr_env[6] = linux_r->fos;
195         bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc));
196 }
197
198 struct linux_pt_fpxreg {
199         l_ushort        cwd;
200         l_ushort        swd;
201         l_ushort        twd;
202         l_ushort        fop;
203         l_long          fip;
204         l_long          fcs;
205         l_long          foo;
206         l_long          fos;
207         l_long          mxcsr;
208         l_long          reserved;
209         l_long          st_space[32];
210         l_long          xmm_space[32];
211         l_long          padding[56];
212 };
213
214 #ifdef CPU_ENABLE_SSE
215 static int
216 linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
217 {
218         int error;
219
220         error = 0;
221         mtx_lock_spin(&sched_lock);
222         if (cpu_fxsr == 0 || (td->td_proc->p_sflag & PS_INMEM) == 0)
223                 error = EIO;
224         else
225                 bcopy(&td->td_pcb->pcb_save.sv_xmm, fpxregs, sizeof(*fpxregs));
226         mtx_unlock_spin(&sched_lock);
227         return (error);
228 }
229
230 static int
231 linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)
232 {
233         int error;
234
235         error = 0;
236         mtx_lock_spin(&sched_lock);
237         if (cpu_fxsr == 0 || (td->td_proc->p_sflag & PS_INMEM) == 0)
238                 error = EIO;
239         else
240                 bcopy(fpxregs, &td->td_pcb->pcb_save.sv_xmm, sizeof(*fpxregs));
241         mtx_unlock_spin(&sched_lock);
242         return (error);
243 }
244 #endif
245
246 int
247 linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)
248 {
249         struct ptrace_args bsd_args;
250         int     error;
251         caddr_t sg;
252         union {
253                 struct linux_pt_reg     reg;
254                 struct linux_pt_fpreg   fpreg;
255                 struct linux_pt_fpxreg  fpxreg;
256         } r;
257
258         sg = stackgap_init();
259
260         error = 0;
261
262         /* by default, just copy data intact */
263         bsd_args.req  = uap->req;
264         bsd_args.pid  = (pid_t)uap->pid;
265         bsd_args.addr = (caddr_t)uap->addr;
266         bsd_args.data = uap->data;
267
268         switch (uap->req) {
269         case PTRACE_TRACEME:
270         case PTRACE_POKETEXT:
271         case PTRACE_POKEDATA:
272         case PTRACE_KILL:
273                 error = ptrace(td, &bsd_args);
274                 break;
275         case PTRACE_PEEKTEXT:
276         case PTRACE_PEEKDATA: {
277                 /* need to preserve return value */
278                 int rval = td->td_retval[0];
279                 bsd_args.data = 0;
280                 error = ptrace(td, &bsd_args);
281                 if (error == 0)
282                         error = copyout(td->td_retval,
283                             (caddr_t)uap->data, sizeof(l_int));
284                 td->td_retval[0] = rval;
285                 break;
286         }
287         case PTRACE_DETACH:
288                 bsd_args.req = PT_DETACH;
289                 /* fall through */
290         case PTRACE_SINGLESTEP:
291         case PTRACE_CONT:
292                 bsd_args.data = map_signum(uap->data);
293                 bsd_args.addr = (caddr_t)1;
294                 error = ptrace(td, &bsd_args);
295                 break;
296         case PTRACE_ATTACH:
297                 bsd_args.req = PT_ATTACH;
298                 error = ptrace(td, &bsd_args);
299                 break;
300         case PTRACE_GETREGS: {
301                 struct reg *bsd_r;
302
303                 bsd_r = (struct reg*)stackgap_alloc(&sg, sizeof(*bsd_r));
304                 /* Linux is using data where FreeBSD is using addr */
305                 bsd_args.req  = PT_GETREGS;
306                 bsd_args.addr = (caddr_t)bsd_r;
307                 bsd_args.data = 0;
308                 error = ptrace(td, &bsd_args);
309                 if (error == 0) {
310                         map_regs_to_linux(bsd_r, &r.reg);
311                         error = copyout(&r.reg, (caddr_t)uap->data,
312                             sizeof(r.reg));
313                 }
314                 break;
315         }
316         case PTRACE_SETREGS: {
317                 struct reg *bsd_r;
318
319                 bsd_r = (struct reg*)stackgap_alloc(&sg, sizeof(*bsd_r));
320                 /* Linux is using data where FreeBSD is using addr */
321                 bsd_args.req  = PT_SETREGS;
322                 bsd_args.addr = (caddr_t)bsd_r;
323                 bsd_args.data = 0;
324                 error = copyin((caddr_t)uap->data, &r.reg, sizeof(r.reg));
325                 if (error == 0) {
326                         map_regs_from_linux(bsd_r, &r.reg);
327                         error = ptrace(td, &bsd_args);
328                 }
329                 break;
330         }
331         case PTRACE_GETFPREGS: {
332                 struct fpreg *bsd_r;
333
334                 bsd_r = (struct fpreg*)stackgap_alloc(&sg, sizeof(*bsd_r));
335                 /* Linux is using data where FreeBSD is using addr */
336                 bsd_args.req  = PT_GETFPREGS;
337                 bsd_args.addr = (caddr_t)bsd_r;
338                 bsd_args.data = 0;
339                 error = ptrace(td, &bsd_args);
340                 if (error == 0) {
341                         map_fpregs_to_linux(bsd_r, &r.fpreg);
342                         error = copyout(&r.fpreg, (caddr_t)uap->data,
343                             sizeof(r.fpreg));
344                 }
345                 break;
346         }
347         case PTRACE_SETFPREGS: {
348                 struct fpreg *bsd_r;
349
350                 bsd_r = (struct fpreg*)stackgap_alloc(&sg, sizeof(*bsd_r));
351                 /* Linux is using data where FreeBSD is using addr */
352                 bsd_args.req  = PT_SETFPREGS;
353                 bsd_args.addr = (caddr_t)bsd_r;
354                 bsd_args.data = 0;
355                 error = copyin((caddr_t)uap->data, &r.fpreg, sizeof(r.fpreg));
356                 if (error == 0) {
357                         map_fpregs_from_linux(bsd_r, &r.fpreg);
358                         error = ptrace(td, &bsd_args);
359                 }
360                 break;
361         }
362         case PTRACE_SETFPXREGS:
363 #ifdef CPU_ENABLE_SSA
364                 error = copyin((caddr_t)uap->data, &r.fpxreg,
365                     sizeof(r.fpxreg));
366                 if (error)
367                         break;
368 #endif
369                 /* FALL THROUGH */
370         case PTRACE_GETFPXREGS: {       
371 #ifdef CPU_ENABLE_SSE
372                 struct proc *p;
373                 struct thread *td2;
374
375                 if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) {
376                         static int once = 0;
377                         if (!once) {
378                                 printf("linux: savexmm != linux_pt_fpxreg\n");
379                                 once = 1;
380                         }
381                         error = EIO;
382                         break;
383                 }
384
385                 if ((p = pfind(uap->pid)) == NULL) {
386                         error = ESRCH;
387                         break;
388                 }
389
390                 if ((error = p_candebug(td, p)) != 0)
391                         goto fail;
392
393                 /* System processes can't be debugged. */
394                 if ((p->p_flag & P_SYSTEM) != 0) {
395                         error = EINVAL;
396                         goto fail;
397                 }
398
399                 /* not being traced... */
400                 if ((p->p_flag & P_TRACED) == 0) {
401                         error = EPERM;
402                         goto fail;
403                 }
404
405                 /* not being traced by YOU */
406                 if (p->p_pptr != td->td_proc) {
407                         error = EBUSY;
408                         goto fail;
409                 }
410
411                 /* not currently stopped */
412                 if (p->p_stat != SSTOP || (p->p_flag & P_WAITED) == 0) {
413                         error = EBUSY;
414                         goto fail;
415                 }
416
417                 td2 = FIRST_THREAD_IN_PROC(p);
418                 if (uap->req == PTRACE_GETFPXREGS) {
419                         _PHOLD(p);
420                         error = linux_proc_read_fpxregs(td2, &r.fpxreg);
421                         _PRELE(p);
422                         PROC_UNLOCK(p);
423                         if (error == 0)
424                                 error = copyout(&r.fpxreg, (caddr_t)uap->data,
425                                     sizeof(r.fpxreg));
426                 } else {
427                         /* clear dangerous bits exactly as Linux does*/
428                         r.fpxreg.mxcsr &= 0xffbf;
429                         _PHOLD(p);
430                         error = linux_proc_write_fpxregs(td2, &r.fpxreg);
431                         _PRELE(p);
432                         PROC_UNLOCK(p);
433                 }
434                 break;
435
436         fail:
437                 PROC_UNLOCK(p);
438 #else
439                 error = EIO;
440 #endif
441                 break;
442         }
443         case PTRACE_PEEKUSR:
444         case PTRACE_POKEUSR: {
445                 error = EIO;
446
447                 /* check addr for alignment */
448                 if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1))
449                         break;
450                 /*
451                  * Allow linux programs to access register values in
452                  * user struct. We simulate this through PT_GET/SETREGS
453                  * as necessary.
454                  */
455                 if (uap->addr < sizeof(struct linux_pt_reg)) {
456                         struct reg *bsd_r;
457
458                         bsd_r = (struct reg*)stackgap_alloc(&sg,
459                             sizeof(*bsd_r));
460                         bsd_args.req  = PT_GETREGS;
461                         bsd_args.addr = (caddr_t)bsd_r;
462                         bsd_args.data = 0;
463
464                         error = ptrace(td, &bsd_args);
465                         if (error != 0)
466                                 break;
467
468                         map_regs_to_linux(bsd_r, &r.reg);
469                         if (uap->req == PTRACE_PEEKUSR) {
470                                 error = copyout((char *)&r.reg + uap->addr,
471                                     (caddr_t)uap->data, sizeof(l_int));
472                                 break;
473                         }
474
475                         *(l_int *)((char *)&r.reg + uap->addr) =
476                             (l_int)uap->data;
477
478                         map_regs_from_linux(bsd_r, &r.reg);
479                         bsd_args.req  = PT_SETREGS;
480                         bsd_args.addr = (caddr_t)bsd_r;
481                         bsd_args.data = 0;
482                         error = ptrace(td, &bsd_args);
483                 }
484                 
485                 /*
486                  * Simulate debug registers access
487                  */
488                 if (uap->addr >= LINUX_DBREG_OFFSET &&
489                     uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {
490                         struct dbreg *bsd_r;
491
492                         bsd_r = (struct dbreg*)stackgap_alloc(&sg,
493                             sizeof(*bsd_r));
494                         bsd_args.req  = PT_GETDBREGS;
495                         bsd_args.addr = (caddr_t)bsd_r;
496                         bsd_args.data = 0;
497                         error = ptrace(td, &bsd_args);
498                         if (error != 0)
499                                 break;
500                         
501                         uap->addr -= LINUX_DBREG_OFFSET;
502                         if (uap->req == PTRACE_PEEKUSR) {
503                                 error = copyout((char *)bsd_r + uap->addr,
504                                     (caddr_t)uap->data, sizeof(l_int));
505                                 break;
506                         }
507
508                         *(l_int *)((char *)bsd_r + uap->addr) = uap->data;
509                         bsd_args.req  = PT_SETDBREGS;
510                         bsd_args.addr = (caddr_t)bsd_r;
511                         bsd_args.data = 0;
512                         error = ptrace(td, &bsd_args);
513                 }
514
515                 break;
516         }
517         case PTRACE_SYSCALL:
518                 /* fall through */
519         default:
520                 printf("linux: ptrace(%u, ...) not implemented\n",
521                     (unsigned int)uap->req);
522                 error = EINVAL;
523                 break;
524         }
525
526         return (error);
527 }