linux_ptrace.c revision 1.11 1 1.11 thorpej /* $NetBSD: linux_ptrace.c,v 1.11 2003/01/18 08:02:48 thorpej Exp $ */
2 1.1 tron
3 1.1 tron /*-
4 1.1 tron * Copyright (c) 1999 The NetBSD Foundation, Inc.
5 1.1 tron * All rights reserved.
6 1.1 tron *
7 1.1 tron * This code is derived from software contributed to The NetBSD Foundation
8 1.1 tron * by Matthias Scheler.
9 1.1 tron *
10 1.1 tron * Redistribution and use in source and binary forms, with or without
11 1.1 tron * modification, are permitted provided that the following conditions
12 1.1 tron * are met:
13 1.1 tron * 1. Redistributions of source code must retain the above copyright
14 1.1 tron * notice, this list of conditions and the following disclaimer.
15 1.1 tron * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 tron * notice, this list of conditions and the following disclaimer in the
17 1.1 tron * documentation and/or other materials provided with the distribution.
18 1.1 tron * 3. All advertising materials mentioning features or use of this software
19 1.1 tron * must display the following acknowledgement:
20 1.1 tron * This product includes software developed by the NetBSD
21 1.1 tron * Foundation, Inc. and its contributors.
22 1.1 tron * 4. Neither the name of The NetBSD Foundation nor the names of its
23 1.1 tron * contributors may be used to endorse or promote products derived
24 1.1 tron * from this software without specific prior written permission.
25 1.1 tron *
26 1.1 tron * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 1.1 tron * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 1.1 tron * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 1.1 tron * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 1.1 tron * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 1.1 tron * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 1.1 tron * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 1.1 tron * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 1.1 tron * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 1.1 tron * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 1.1 tron * POSSIBILITY OF SUCH DAMAGE.
37 1.1 tron */
38 1.7 lukem
39 1.7 lukem #include <sys/cdefs.h>
40 1.11 thorpej __KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.11 2003/01/18 08:02:48 thorpej Exp $");
41 1.1 tron
42 1.1 tron #include <sys/param.h>
43 1.5 jdolecek #include <sys/malloc.h>
44 1.1 tron #include <sys/mount.h>
45 1.1 tron #include <sys/proc.h>
46 1.1 tron #include <sys/ptrace.h>
47 1.1 tron #include <sys/systm.h>
48 1.11 thorpej #include <sys/sa.h>
49 1.1 tron #include <sys/syscallargs.h>
50 1.5 jdolecek #include <uvm/uvm_extern.h>
51 1.1 tron
52 1.3 tron #include <machine/reg.h>
53 1.3 tron
54 1.1 tron #include <compat/linux/common/linux_types.h>
55 1.1 tron #include <compat/linux/common/linux_ptrace.h>
56 1.1 tron #include <compat/linux/common/linux_signal.h>
57 1.1 tron
58 1.1 tron #include <compat/linux/common/linux_util.h>
59 1.1 tron #include <compat/linux/common/linux_machdep.h>
60 1.5 jdolecek #include <compat/linux/common/linux_emuldata.h>
61 1.5 jdolecek #include <compat/linux/common/linux_exec.h> /* for emul_linux */
62 1.1 tron
63 1.1 tron #include <compat/linux/linux_syscallargs.h>
64 1.2 tron
65 1.5 jdolecek #include <lib/libkern/libkern.h> /* for offsetof() */
66 1.5 jdolecek
67 1.2 tron struct linux_reg {
68 1.2 tron long ebx;
69 1.2 tron long ecx;
70 1.2 tron long edx;
71 1.2 tron long esi;
72 1.2 tron long edi;
73 1.2 tron long ebp;
74 1.2 tron long eax;
75 1.6 lukem long xds, xes; /* unsigned short ds, __ds, es, __es; */
76 1.6 lukem long __fs, __gs; /* unsigned short fs, __fs, gs, __gs; */
77 1.2 tron long orig_eax;
78 1.2 tron long eip;
79 1.6 lukem long xcs; /* unsigned short cs, __cs; */
80 1.2 tron long eflags;
81 1.2 tron long esp;
82 1.6 lukem long xss; /* unsigned short ss, __ss; */
83 1.5 jdolecek };
84 1.5 jdolecek
85 1.5 jdolecek /* structure used for storing floating point context */
86 1.10 junyoung struct linux_fpctx {
87 1.10 junyoung long cwd;
88 1.10 junyoung long swd;
89 1.10 junyoung long twd;
90 1.10 junyoung long fip;
91 1.10 junyoung long fcs;
92 1.10 junyoung long foo;
93 1.10 junyoung long fos;
94 1.10 junyoung long st_space[20];
95 1.2 tron };
96 1.2 tron
97 1.5 jdolecek /* user struct for linux process - this is used for Linux ptrace emulation */
98 1.5 jdolecek /* most of it is junk only used by gdb */
99 1.5 jdolecek struct linux_user {
100 1.10 junyoung struct linux_reg regs; /* registers */
101 1.10 junyoung int u_fpvalid; /* true if math co-processor being used. */
102 1.10 junyoung struct linux_fpctx i387; /* Math Co-processor registers. */
103 1.5 jdolecek /* The rest of this junk is to help gdb figure out what goes where */
104 1.5 jdolecek #define lusr_startgdb u_tsize
105 1.10 junyoung unsigned long int u_tsize; /* Text segment size (pages). */
106 1.10 junyoung unsigned long int u_dsize; /* Data segment size (pages). */
107 1.10 junyoung unsigned long int u_ssize; /* Stack segment size (pages). */
108 1.10 junyoung unsigned long start_code; /* Starting virtual address of text. */
109 1.10 junyoung unsigned long start_stack; /* Starting virtual address of stack
110 1.10 junyoung area. This is actually the bottom of
111 1.10 junyoung the stack, the top of the stack is
112 1.10 junyoung always found in the esp register. */
113 1.10 junyoung long int __signal; /* Signal that caused the core dump. */
114 1.10 junyoung int __reserved; /* unused */
115 1.10 junyoung void *u_ar0; /* Used by gdb to help find the values
116 1.10 junyoung for the registers. */
117 1.10 junyoung struct linux_fpctx *u_fpstate; /* Math Co-processor pointer. */
118 1.10 junyoung unsigned long __magic; /* To uniquely identify a core file */
119 1.10 junyoung char u_comm[32]; /* User command that was responsible */
120 1.10 junyoung int u_debugreg[8];
121 1.5 jdolecek #define u_debugreg_end u_debugreg[7]
122 1.5 jdolecek };
123 1.5 jdolecek
124 1.5 jdolecek #define LUSR_OFF(member) offsetof(struct linux_user, member)
125 1.5 jdolecek #define ISSET(t, f) ((t) & (f))
126 1.3 tron
127 1.1 tron int
128 1.11 thorpej linux_sys_ptrace_arch(l, v, retval)
129 1.11 thorpej struct lwp *l;
130 1.1 tron void *v;
131 1.1 tron register_t *retval;
132 1.1 tron {
133 1.1 tron struct linux_sys_ptrace_args /* {
134 1.1 tron syscallarg(int) request;
135 1.1 tron syscallarg(int) pid;
136 1.1 tron syscallarg(int) addr;
137 1.1 tron syscallarg(int) data;
138 1.1 tron } */ *uap = v;
139 1.11 thorpej struct proc *p = l->l_proc;
140 1.3 tron int request, error;
141 1.3 tron struct proc *t; /* target process */
142 1.11 thorpej struct lwp *lt;
143 1.5 jdolecek struct reg *regs = NULL;
144 1.5 jdolecek struct fpreg *fpregs = NULL;
145 1.5 jdolecek struct linux_reg *linux_regs = NULL;
146 1.10 junyoung struct linux_fpctx *linux_fpregs = NULL;
147 1.5 jdolecek int addr;
148 1.3 tron
149 1.3 tron request = SCARG(uap, request);
150 1.3 tron
151 1.5 jdolecek if ((request != LINUX_PTRACE_PEEKUSR) &&
152 1.5 jdolecek (request != LINUX_PTRACE_POKEUSR) &&
153 1.5 jdolecek (request != LINUX_PTRACE_GETREGS) &&
154 1.3 tron (request != LINUX_PTRACE_SETREGS) &&
155 1.3 tron (request != LINUX_PTRACE_GETFPREGS) &&
156 1.3 tron (request != LINUX_PTRACE_SETFPREGS))
157 1.3 tron return EIO;
158 1.1 tron
159 1.3 tron /* Find the process we're supposed to be operating on. */
160 1.3 tron if ((t = pfind(SCARG(uap, pid))) == NULL)
161 1.3 tron return ESRCH;
162 1.3 tron
163 1.3 tron /*
164 1.3 tron * You can't do what you want to the process if:
165 1.3 tron * (1) It's not being traced at all,
166 1.3 tron */
167 1.3 tron if (!ISSET(t->p_flag, P_TRACED))
168 1.3 tron return EPERM;
169 1.3 tron
170 1.3 tron /*
171 1.3 tron * (2) it's being traced by procfs (which has
172 1.3 tron * different signal delivery semantics),
173 1.3 tron */
174 1.3 tron if (ISSET(t->p_flag, P_FSTRACE))
175 1.3 tron return EBUSY;
176 1.3 tron
177 1.3 tron /*
178 1.3 tron * (3) it's not being traced by _you_, or
179 1.3 tron */
180 1.3 tron if (t->p_pptr != p)
181 1.3 tron return EBUSY;
182 1.3 tron
183 1.3 tron /*
184 1.3 tron * (4) it's not currently stopped.
185 1.3 tron */
186 1.3 tron if (t->p_stat != SSTOP || !ISSET(t->p_flag, P_WAITED))
187 1.3 tron return EBUSY;
188 1.3 tron
189 1.11 thorpej /* XXX NJWLWP
190 1.11 thorpej * The entire ptrace interface needs work to be useful to
191 1.11 thorpej * a process with multiple LWPs. For the moment, we'll
192 1.11 thorpej * just kluge this and fail on others.
193 1.11 thorpej */
194 1.11 thorpej
195 1.11 thorpej if (p->p_nlwps > 1)
196 1.11 thorpej return (ENOSYS);
197 1.11 thorpej
198 1.11 thorpej lt = LIST_FIRST(&t->p_lwps);
199 1.11 thorpej
200 1.3 tron *retval = 0;
201 1.3 tron
202 1.3 tron switch (request) {
203 1.3 tron case LINUX_PTRACE_GETREGS:
204 1.5 jdolecek MALLOC(regs, struct reg*, sizeof(struct reg), M_TEMP, M_WAITOK);
205 1.5 jdolecek MALLOC(linux_regs, struct linux_reg*, sizeof(struct linux_reg),
206 1.5 jdolecek M_TEMP, M_WAITOK);
207 1.5 jdolecek
208 1.11 thorpej error = process_read_regs(lt, regs);
209 1.3 tron if (error != 0)
210 1.5 jdolecek goto out;
211 1.3 tron
212 1.5 jdolecek linux_regs->ebx = regs->r_ebx;
213 1.5 jdolecek linux_regs->ecx = regs->r_ecx;
214 1.5 jdolecek linux_regs->edx = regs->r_edx;
215 1.5 jdolecek linux_regs->esi = regs->r_esi;
216 1.5 jdolecek linux_regs->edi = regs->r_edi;
217 1.5 jdolecek linux_regs->ebp = regs->r_ebp;
218 1.5 jdolecek linux_regs->eax = regs->r_eax;
219 1.5 jdolecek linux_regs->xds = regs->r_ds;
220 1.5 jdolecek linux_regs->xes = regs->r_es;
221 1.5 jdolecek linux_regs->orig_eax = regs->r_eax; /* XXX is this correct? */
222 1.5 jdolecek linux_regs->eip = regs->r_cs + regs->r_eip;
223 1.5 jdolecek linux_regs->xcs = regs->r_cs;
224 1.5 jdolecek linux_regs->eflags = regs->r_eflags;
225 1.5 jdolecek linux_regs->esp = regs->r_esp;
226 1.5 jdolecek linux_regs->xss = regs->r_ss;
227 1.3 tron
228 1.5 jdolecek error = copyout(linux_regs, (caddr_t)SCARG(uap, data),
229 1.3 tron sizeof(struct linux_reg));
230 1.5 jdolecek goto out;
231 1.5 jdolecek
232 1.3 tron case LINUX_PTRACE_SETREGS:
233 1.5 jdolecek MALLOC(regs, struct reg*, sizeof(struct reg), M_TEMP, M_WAITOK);
234 1.5 jdolecek MALLOC(linux_regs, struct linux_reg *, sizeof(struct linux_reg),
235 1.5 jdolecek M_TEMP, M_WAITOK);
236 1.5 jdolecek
237 1.5 jdolecek error = copyin((caddr_t)SCARG(uap, data), linux_regs,
238 1.3 tron sizeof(struct linux_reg));
239 1.3 tron if (error != 0)
240 1.5 jdolecek goto out;
241 1.5 jdolecek
242 1.5 jdolecek regs->r_ebx = linux_regs->ebx;
243 1.5 jdolecek regs->r_ecx = linux_regs->ecx;
244 1.5 jdolecek regs->r_edx = linux_regs->edx;
245 1.5 jdolecek regs->r_esi = linux_regs->esi;
246 1.5 jdolecek regs->r_edi = linux_regs->edi;
247 1.5 jdolecek regs->r_ebp = linux_regs->ebp;
248 1.5 jdolecek regs->r_eax = linux_regs->eax;
249 1.5 jdolecek regs->r_ds = linux_regs->xds;
250 1.5 jdolecek regs->r_es = linux_regs->xes;
251 1.5 jdolecek regs->r_eip = linux_regs->eip - linux_regs->xcs;
252 1.5 jdolecek regs->r_cs = linux_regs->xcs;
253 1.5 jdolecek regs->r_eflags = linux_regs->eflags;
254 1.5 jdolecek regs->r_esp = linux_regs->esp;
255 1.5 jdolecek regs->r_ss = linux_regs->xss;
256 1.5 jdolecek
257 1.11 thorpej error = process_write_regs(lt, regs);
258 1.5 jdolecek goto out;
259 1.5 jdolecek
260 1.5 jdolecek case LINUX_PTRACE_GETFPREGS:
261 1.5 jdolecek MALLOC(fpregs, struct fpreg *, sizeof(struct fpreg),
262 1.5 jdolecek M_TEMP, M_WAITOK);
263 1.10 junyoung MALLOC(linux_fpregs, struct linux_fpctx *,
264 1.10 junyoung sizeof(struct linux_fpctx), M_TEMP, M_WAITOK);
265 1.5 jdolecek
266 1.11 thorpej error = process_read_fpregs(lt, fpregs);
267 1.5 jdolecek if (error != 0)
268 1.5 jdolecek goto out;
269 1.3 tron
270 1.5 jdolecek /* zero the contents if NetBSD fpreg structure is smaller */
271 1.10 junyoung if (sizeof(struct fpreg) < sizeof(struct linux_fpctx))
272 1.10 junyoung memset(linux_fpregs, '\0', sizeof(struct linux_fpctx));
273 1.5 jdolecek
274 1.5 jdolecek memcpy(linux_fpregs, fpregs,
275 1.10 junyoung min(sizeof(struct linux_fpctx), sizeof(struct fpreg)));
276 1.5 jdolecek error = copyout(linux_fpregs, (caddr_t)SCARG(uap, data),
277 1.10 junyoung sizeof(struct linux_fpctx));
278 1.5 jdolecek goto out;
279 1.5 jdolecek
280 1.5 jdolecek case LINUX_PTRACE_SETFPREGS:
281 1.5 jdolecek MALLOC(fpregs, struct fpreg *, sizeof(struct fpreg),
282 1.5 jdolecek M_TEMP, M_WAITOK);
283 1.10 junyoung MALLOC(linux_fpregs, struct linux_fpctx *,
284 1.10 junyoung sizeof(struct linux_fpctx), M_TEMP, M_WAITOK);
285 1.5 jdolecek error = copyin((caddr_t)SCARG(uap, data), linux_fpregs,
286 1.10 junyoung sizeof(struct linux_fpctx));
287 1.5 jdolecek if (error != 0)
288 1.5 jdolecek goto out;
289 1.1 tron
290 1.5 jdolecek memset(fpregs, '\0', sizeof(struct fpreg));
291 1.5 jdolecek memcpy(fpregs, linux_fpregs,
292 1.10 junyoung min(sizeof(struct linux_fpctx), sizeof(struct fpreg)));
293 1.5 jdolecek
294 1.11 thorpej error = process_write_regs(lt, regs);
295 1.5 jdolecek goto out;
296 1.5 jdolecek
297 1.5 jdolecek case LINUX_PTRACE_PEEKUSR:
298 1.5 jdolecek addr = SCARG(uap, addr);
299 1.5 jdolecek
300 1.11 thorpej PHOLD(lt); /* need full process info */
301 1.5 jdolecek error = 0;
302 1.5 jdolecek if (addr < LUSR_OFF(lusr_startgdb)) {
303 1.5 jdolecek /* XXX should provide appropriate register */
304 1.5 jdolecek error = 1;
305 1.5 jdolecek } else if (addr == LUSR_OFF(u_tsize))
306 1.5 jdolecek *retval = p->p_vmspace->vm_tsize;
307 1.5 jdolecek else if (addr == LUSR_OFF(u_dsize))
308 1.5 jdolecek *retval = p->p_vmspace->vm_dsize;
309 1.5 jdolecek else if (addr == LUSR_OFF(u_ssize))
310 1.5 jdolecek *retval = p->p_vmspace->vm_ssize;
311 1.5 jdolecek else if (addr == LUSR_OFF(start_code))
312 1.5 jdolecek *retval = (register_t) p->p_vmspace->vm_taddr;
313 1.5 jdolecek else if (addr == LUSR_OFF(start_stack))
314 1.5 jdolecek *retval = (register_t) p->p_vmspace->vm_minsaddr;
315 1.5 jdolecek else if (addr == LUSR_OFF(u_ar0))
316 1.5 jdolecek *retval = LUSR_OFF(regs);
317 1.5 jdolecek else if (addr >= LUSR_OFF(u_debugreg)
318 1.5 jdolecek && addr <= LUSR_OFF(u_debugreg_end)) {
319 1.5 jdolecek int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
320 1.5 jdolecek
321 1.5 jdolecek /* only do this for Linux processes */
322 1.5 jdolecek if (t->p_emul != &emul_linux)
323 1.5 jdolecek error = EINVAL;
324 1.5 jdolecek else {
325 1.5 jdolecek *retval = ((struct linux_emuldata *)
326 1.5 jdolecek t->p_emuldata)->debugreg[off];
327 1.5 jdolecek }
328 1.5 jdolecek } else if (addr == LUSR_OFF(__signal)) {
329 1.5 jdolecek error = 1;
330 1.5 jdolecek } else if (addr == LUSR_OFF(__signal)) {
331 1.5 jdolecek error = 1;
332 1.5 jdolecek } else if (addr == LUSR_OFF(u_fpstate)) {
333 1.5 jdolecek error = 1;
334 1.5 jdolecek } else if (addr == LUSR_OFF(__magic)) {
335 1.5 jdolecek error = 1;
336 1.5 jdolecek } else if (addr == LUSR_OFF(u_comm)) {
337 1.5 jdolecek error = 1;
338 1.5 jdolecek } else {
339 1.5 jdolecek #ifdef DEBUG_LINUX
340 1.5 jdolecek printf("linux_ptrace: unsupported address: %d\n", addr);
341 1.5 jdolecek #endif
342 1.5 jdolecek error = 1;
343 1.5 jdolecek }
344 1.5 jdolecek
345 1.11 thorpej PRELE(lt);
346 1.5 jdolecek
347 1.5 jdolecek if (!error)
348 1.5 jdolecek return 0;
349 1.5 jdolecek
350 1.5 jdolecek case LINUX_PTRACE_POKEUSR:
351 1.5 jdolecek /* we only support setting debugregs for now */
352 1.5 jdolecek addr = SCARG(uap, addr);
353 1.5 jdolecek if (addr >= LUSR_OFF(u_debugreg)
354 1.5 jdolecek && addr <= LUSR_OFF(u_debugreg_end)) {
355 1.5 jdolecek int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
356 1.5 jdolecek int data = SCARG(uap, data);
357 1.5 jdolecek
358 1.5 jdolecek /* only do this for Linux processes */
359 1.5 jdolecek if (t->p_emul != &emul_linux)
360 1.5 jdolecek return EINVAL;
361 1.5 jdolecek
362 1.11 thorpej PHOLD(lt);
363 1.5 jdolecek ((struct linux_emuldata *)t->p_emuldata)->debugreg[off] = data;
364 1.11 thorpej PRELE(lt);
365 1.5 jdolecek return (0);
366 1.5 jdolecek }
367 1.5 jdolecek
368 1.5 jdolecek break;
369 1.5 jdolecek default:
370 1.5 jdolecek /* never reached */
371 1.5 jdolecek break;
372 1.1 tron }
373 1.1 tron
374 1.3 tron return EIO;
375 1.5 jdolecek
376 1.5 jdolecek out:
377 1.5 jdolecek if (regs)
378 1.5 jdolecek FREE(regs, M_TEMP);
379 1.5 jdolecek if (fpregs)
380 1.5 jdolecek FREE(fpregs, M_TEMP);
381 1.5 jdolecek if (linux_regs)
382 1.5 jdolecek FREE(linux_regs, M_TEMP);
383 1.5 jdolecek if (linux_fpregs)
384 1.5 jdolecek FREE(linux_fpregs, M_TEMP);
385 1.5 jdolecek return (error);
386 1.1 tron }
387