linux_ptrace.c revision 1.36 1 1.36 tsutsui /* $NetBSD: linux_ptrace.c,v 1.36 2022/09/05 14:14:42 tsutsui 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 *
19 1.1 tron * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 tron * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 tron * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 tron * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 tron * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 tron * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 tron * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 tron * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 tron * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 tron * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 tron * POSSIBILITY OF SUCH DAMAGE.
30 1.1 tron */
31 1.7 lukem
32 1.7 lukem #include <sys/cdefs.h>
33 1.36 tsutsui __KERNEL_RCSID(0, "$NetBSD: linux_ptrace.c,v 1.36 2022/09/05 14:14:42 tsutsui Exp $");
34 1.1 tron
35 1.1 tron #include <sys/param.h>
36 1.1 tron #include <sys/mount.h>
37 1.1 tron #include <sys/proc.h>
38 1.1 tron #include <sys/ptrace.h>
39 1.1 tron #include <sys/systm.h>
40 1.15 matt #include <sys/syscall.h>
41 1.1 tron #include <sys/syscallargs.h>
42 1.5 jdolecek #include <uvm/uvm_extern.h>
43 1.1 tron
44 1.3 tron #include <machine/reg.h>
45 1.3 tron
46 1.1 tron #include <compat/linux/common/linux_types.h>
47 1.1 tron #include <compat/linux/common/linux_ptrace.h>
48 1.1 tron #include <compat/linux/common/linux_signal.h>
49 1.1 tron
50 1.1 tron #include <compat/linux/common/linux_util.h>
51 1.1 tron #include <compat/linux/common/linux_machdep.h>
52 1.5 jdolecek #include <compat/linux/common/linux_emuldata.h>
53 1.5 jdolecek #include <compat/linux/common/linux_exec.h> /* for emul_linux */
54 1.1 tron
55 1.1 tron #include <compat/linux/linux_syscallargs.h>
56 1.2 tron
57 1.5 jdolecek #include <lib/libkern/libkern.h> /* for offsetof() */
58 1.5 jdolecek
59 1.2 tron struct linux_reg {
60 1.2 tron long ebx;
61 1.2 tron long ecx;
62 1.2 tron long edx;
63 1.2 tron long esi;
64 1.2 tron long edi;
65 1.2 tron long ebp;
66 1.2 tron long eax;
67 1.6 lukem long xds, xes; /* unsigned short ds, __ds, es, __es; */
68 1.6 lukem long __fs, __gs; /* unsigned short fs, __fs, gs, __gs; */
69 1.2 tron long orig_eax;
70 1.2 tron long eip;
71 1.6 lukem long xcs; /* unsigned short cs, __cs; */
72 1.2 tron long eflags;
73 1.2 tron long esp;
74 1.6 lukem long xss; /* unsigned short ss, __ss; */
75 1.5 jdolecek };
76 1.5 jdolecek
77 1.5 jdolecek /* structure used for storing floating point context */
78 1.10 junyoung struct linux_fpctx {
79 1.10 junyoung long cwd;
80 1.10 junyoung long swd;
81 1.10 junyoung long twd;
82 1.10 junyoung long fip;
83 1.10 junyoung long fcs;
84 1.10 junyoung long foo;
85 1.10 junyoung long fos;
86 1.10 junyoung long st_space[20];
87 1.2 tron };
88 1.2 tron
89 1.5 jdolecek /* user struct for linux process - this is used for Linux ptrace emulation */
90 1.5 jdolecek /* most of it is junk only used by gdb */
91 1.5 jdolecek struct linux_user {
92 1.10 junyoung struct linux_reg regs; /* registers */
93 1.10 junyoung int u_fpvalid; /* true if math co-processor being used. */
94 1.10 junyoung struct linux_fpctx i387; /* Math Co-processor registers. */
95 1.5 jdolecek /* The rest of this junk is to help gdb figure out what goes where */
96 1.5 jdolecek #define lusr_startgdb u_tsize
97 1.10 junyoung unsigned long int u_tsize; /* Text segment size (pages). */
98 1.10 junyoung unsigned long int u_dsize; /* Data segment size (pages). */
99 1.10 junyoung unsigned long int u_ssize; /* Stack segment size (pages). */
100 1.10 junyoung unsigned long start_code; /* Starting virtual address of text. */
101 1.12 perry unsigned long start_stack; /* Starting virtual address of stack
102 1.10 junyoung area. This is actually the bottom of
103 1.10 junyoung the stack, the top of the stack is
104 1.10 junyoung always found in the esp register. */
105 1.10 junyoung long int __signal; /* Signal that caused the core dump. */
106 1.10 junyoung int __reserved; /* unused */
107 1.10 junyoung void *u_ar0; /* Used by gdb to help find the values
108 1.10 junyoung for the registers. */
109 1.10 junyoung struct linux_fpctx *u_fpstate; /* Math Co-processor pointer. */
110 1.10 junyoung unsigned long __magic; /* To uniquely identify a core file */
111 1.10 junyoung char u_comm[32]; /* User command that was responsible */
112 1.10 junyoung int u_debugreg[8];
113 1.5 jdolecek #define u_debugreg_end u_debugreg[7]
114 1.5 jdolecek };
115 1.5 jdolecek
116 1.5 jdolecek #define LUSR_OFF(member) offsetof(struct linux_user, member)
117 1.3 tron
118 1.21 ad int linux_ptrace_disabled = 1; /* bitrotted */
119 1.21 ad
120 1.1 tron int
121 1.25 rmind linux_sys_ptrace_arch(struct lwp *l, const struct linux_sys_ptrace_args *uap,
122 1.25 rmind register_t *retval)
123 1.1 tron {
124 1.20 dsl /* {
125 1.1 tron syscallarg(int) request;
126 1.1 tron syscallarg(int) pid;
127 1.1 tron syscallarg(int) addr;
128 1.1 tron syscallarg(int) data;
129 1.20 dsl } */
130 1.25 rmind struct proc *p = l->l_proc, *t;
131 1.11 thorpej struct lwp *lt;
132 1.5 jdolecek struct reg *regs = NULL;
133 1.5 jdolecek struct fpreg *fpregs = NULL;
134 1.5 jdolecek struct linux_reg *linux_regs = NULL;
135 1.10 junyoung struct linux_fpctx *linux_fpregs = NULL;
136 1.26 chs struct linux_emuldata *led;
137 1.25 rmind int request, error, addr;
138 1.27 dsl size_t fp_size;
139 1.3 tron
140 1.21 ad if (linux_ptrace_disabled)
141 1.21 ad return ENOSYS;
142 1.21 ad
143 1.25 rmind error = 0;
144 1.3 tron request = SCARG(uap, request);
145 1.3 tron
146 1.25 rmind switch (request) {
147 1.25 rmind case LINUX_PTRACE_PEEKUSR:
148 1.25 rmind case LINUX_PTRACE_POKEUSR:
149 1.25 rmind break;
150 1.25 rmind case LINUX_PTRACE_GETREGS:
151 1.25 rmind case LINUX_PTRACE_SETREGS:
152 1.25 rmind regs = kmem_alloc(sizeof(struct reg), KM_SLEEP);
153 1.25 rmind linux_regs = kmem_alloc(sizeof(struct linux_reg), KM_SLEEP);
154 1.25 rmind if (request == LINUX_PTRACE_SETREGS) {
155 1.25 rmind error = copyin((void *)SCARG(uap, data), linux_regs,
156 1.25 rmind sizeof(struct linux_reg));
157 1.25 rmind if (error) {
158 1.25 rmind goto out;
159 1.25 rmind }
160 1.25 rmind }
161 1.25 rmind break;
162 1.25 rmind case LINUX_PTRACE_GETFPREGS:
163 1.25 rmind case LINUX_PTRACE_SETFPREGS:
164 1.25 rmind fpregs = kmem_alloc(sizeof(struct fpreg), KM_SLEEP);
165 1.25 rmind linux_fpregs = kmem_alloc(sizeof(struct linux_fpctx), KM_SLEEP);
166 1.25 rmind if (request == LINUX_PTRACE_SETFPREGS) {
167 1.25 rmind error = copyin((void *)SCARG(uap, data), linux_fpregs,
168 1.25 rmind sizeof(struct linux_fpctx));
169 1.25 rmind if (error) {
170 1.25 rmind goto out;
171 1.25 rmind }
172 1.25 rmind }
173 1.25 rmind break;
174 1.25 rmind default:
175 1.25 rmind error = EIO;
176 1.25 rmind goto out;
177 1.25 rmind }
178 1.1 tron
179 1.25 rmind /* Find the process we are supposed to be operating on. */
180 1.34 ad mutex_enter(&proc_lock);
181 1.25 rmind if ((t = proc_find(SCARG(uap, pid))) == NULL) {
182 1.34 ad mutex_exit(&proc_lock);
183 1.29 christos error = ESRCH;
184 1.29 christos goto out;
185 1.25 rmind }
186 1.25 rmind mutex_enter(t->p_lock);
187 1.3 tron
188 1.3 tron /*
189 1.25 rmind * You cannot do what you want to the process if:
190 1.25 rmind * 1. It is not being traced at all,
191 1.3 tron */
192 1.25 rmind if (!ISSET(t->p_slflag, PSL_TRACED)) {
193 1.25 rmind mutex_exit(t->p_lock);
194 1.34 ad mutex_exit(&proc_lock);
195 1.25 rmind error = EPERM;
196 1.25 rmind goto out;
197 1.25 rmind }
198 1.3 tron /*
199 1.32 kamil * 2. It is not being traced by _you_, or
200 1.32 kamil * 3. It is not currently stopped.
201 1.3 tron */
202 1.32 kamil if (t->p_pptr != p || t->p_stat != SSTOP || !t->p_waited) {
203 1.25 rmind mutex_exit(t->p_lock);
204 1.34 ad mutex_exit(&proc_lock);
205 1.25 rmind error = EBUSY;
206 1.25 rmind goto out;
207 1.25 rmind }
208 1.34 ad mutex_exit(&proc_lock);
209 1.25 rmind /* XXX: ptrace needs revamp for multi-threading support. */
210 1.25 rmind if (t->p_nlwps > 1) {
211 1.25 rmind mutex_exit(t->p_lock);
212 1.25 rmind error = ENOSYS;
213 1.25 rmind goto out;
214 1.25 rmind }
215 1.11 thorpej lt = LIST_FIRST(&t->p_lwps);
216 1.3 tron *retval = 0;
217 1.3 tron
218 1.3 tron switch (request) {
219 1.25 rmind case LINUX_PTRACE_GETREGS:
220 1.11 thorpej error = process_read_regs(lt, regs);
221 1.25 rmind mutex_exit(t->p_lock);
222 1.25 rmind if (error) {
223 1.25 rmind break;
224 1.25 rmind }
225 1.35 riastrad memset(linux_regs, 0, sizeof(*linux_regs));
226 1.5 jdolecek linux_regs->ebx = regs->r_ebx;
227 1.5 jdolecek linux_regs->ecx = regs->r_ecx;
228 1.5 jdolecek linux_regs->edx = regs->r_edx;
229 1.5 jdolecek linux_regs->esi = regs->r_esi;
230 1.5 jdolecek linux_regs->edi = regs->r_edi;
231 1.5 jdolecek linux_regs->ebp = regs->r_ebp;
232 1.5 jdolecek linux_regs->eax = regs->r_eax;
233 1.5 jdolecek linux_regs->xds = regs->r_ds;
234 1.5 jdolecek linux_regs->xes = regs->r_es;
235 1.5 jdolecek linux_regs->orig_eax = regs->r_eax; /* XXX is this correct? */
236 1.5 jdolecek linux_regs->eip = regs->r_cs + regs->r_eip;
237 1.5 jdolecek linux_regs->xcs = regs->r_cs;
238 1.5 jdolecek linux_regs->eflags = regs->r_eflags;
239 1.5 jdolecek linux_regs->esp = regs->r_esp;
240 1.5 jdolecek linux_regs->xss = regs->r_ss;
241 1.3 tron
242 1.17 christos error = copyout(linux_regs, (void *)SCARG(uap, data),
243 1.3 tron sizeof(struct linux_reg));
244 1.25 rmind break;
245 1.5 jdolecek
246 1.25 rmind case LINUX_PTRACE_SETREGS:
247 1.5 jdolecek regs->r_ebx = linux_regs->ebx;
248 1.5 jdolecek regs->r_ecx = linux_regs->ecx;
249 1.5 jdolecek regs->r_edx = linux_regs->edx;
250 1.5 jdolecek regs->r_esi = linux_regs->esi;
251 1.5 jdolecek regs->r_edi = linux_regs->edi;
252 1.5 jdolecek regs->r_ebp = linux_regs->ebp;
253 1.5 jdolecek regs->r_eax = linux_regs->eax;
254 1.5 jdolecek regs->r_ds = linux_regs->xds;
255 1.5 jdolecek regs->r_es = linux_regs->xes;
256 1.5 jdolecek regs->r_eip = linux_regs->eip - linux_regs->xcs;
257 1.5 jdolecek regs->r_cs = linux_regs->xcs;
258 1.5 jdolecek regs->r_eflags = linux_regs->eflags;
259 1.5 jdolecek regs->r_esp = linux_regs->esp;
260 1.5 jdolecek regs->r_ss = linux_regs->xss;
261 1.5 jdolecek
262 1.11 thorpej error = process_write_regs(lt, regs);
263 1.25 rmind mutex_exit(t->p_lock);
264 1.25 rmind break;
265 1.5 jdolecek
266 1.25 rmind case LINUX_PTRACE_GETFPREGS:
267 1.27 dsl fp_size = sizeof fpregs;
268 1.27 dsl error = process_read_fpregs(lt, fpregs, &fp_size);
269 1.25 rmind mutex_exit(t->p_lock);
270 1.25 rmind if (error) {
271 1.25 rmind break;
272 1.25 rmind }
273 1.25 rmind /* Zero the contents if NetBSD fpreg structure is smaller */
274 1.27 dsl if (fp_size < sizeof(struct linux_fpctx)) {
275 1.10 junyoung memset(linux_fpregs, '\0', sizeof(struct linux_fpctx));
276 1.25 rmind }
277 1.5 jdolecek memcpy(linux_fpregs, fpregs,
278 1.33 riastrad uimin(sizeof(struct linux_fpctx), fp_size));
279 1.17 christos error = copyout(linux_fpregs, (void *)SCARG(uap, data),
280 1.10 junyoung sizeof(struct linux_fpctx));
281 1.25 rmind break;
282 1.1 tron
283 1.25 rmind case LINUX_PTRACE_SETFPREGS:
284 1.5 jdolecek memset(fpregs, '\0', sizeof(struct fpreg));
285 1.5 jdolecek memcpy(fpregs, linux_fpregs,
286 1.33 riastrad uimin(sizeof(struct linux_fpctx), sizeof(struct fpreg)));
287 1.11 thorpej error = process_write_regs(lt, regs);
288 1.25 rmind mutex_exit(t->p_lock);
289 1.25 rmind break;
290 1.5 jdolecek
291 1.25 rmind case LINUX_PTRACE_PEEKUSR:
292 1.25 rmind /* XXX locking */
293 1.5 jdolecek addr = SCARG(uap, addr);
294 1.5 jdolecek
295 1.5 jdolecek error = 0;
296 1.5 jdolecek if (addr < LUSR_OFF(lusr_startgdb)) {
297 1.5 jdolecek /* XXX should provide appropriate register */
298 1.26 chs error = ENOTSUP;
299 1.5 jdolecek } else if (addr == LUSR_OFF(u_tsize))
300 1.5 jdolecek *retval = p->p_vmspace->vm_tsize;
301 1.5 jdolecek else if (addr == LUSR_OFF(u_dsize))
302 1.5 jdolecek *retval = p->p_vmspace->vm_dsize;
303 1.5 jdolecek else if (addr == LUSR_OFF(u_ssize))
304 1.5 jdolecek *retval = p->p_vmspace->vm_ssize;
305 1.5 jdolecek else if (addr == LUSR_OFF(start_code))
306 1.5 jdolecek *retval = (register_t) p->p_vmspace->vm_taddr;
307 1.5 jdolecek else if (addr == LUSR_OFF(start_stack))
308 1.5 jdolecek *retval = (register_t) p->p_vmspace->vm_minsaddr;
309 1.5 jdolecek else if (addr == LUSR_OFF(u_ar0))
310 1.5 jdolecek *retval = LUSR_OFF(regs);
311 1.5 jdolecek else if (addr >= LUSR_OFF(u_debugreg)
312 1.5 jdolecek && addr <= LUSR_OFF(u_debugreg_end)) {
313 1.5 jdolecek int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
314 1.5 jdolecek
315 1.5 jdolecek /* only do this for Linux processes */
316 1.26 chs if (t->p_emul != &emul_linux) {
317 1.26 chs mutex_exit(t->p_lock);
318 1.26 chs return EINVAL;
319 1.5 jdolecek }
320 1.26 chs
321 1.26 chs led = lt->l_emuldata;
322 1.26 chs *retval = led->led_debugreg[off];
323 1.5 jdolecek } else if (addr == LUSR_OFF(__signal)) {
324 1.26 chs error = ENOTSUP;
325 1.5 jdolecek } else if (addr == LUSR_OFF(u_fpstate)) {
326 1.26 chs error = ENOTSUP;
327 1.5 jdolecek } else if (addr == LUSR_OFF(__magic)) {
328 1.26 chs error = ENOTSUP;
329 1.5 jdolecek } else if (addr == LUSR_OFF(u_comm)) {
330 1.26 chs error = ENOTSUP;
331 1.5 jdolecek } else {
332 1.5 jdolecek #ifdef DEBUG_LINUX
333 1.5 jdolecek printf("linux_ptrace: unsupported address: %d\n", addr);
334 1.5 jdolecek #endif
335 1.26 chs error = ENOTSUP;
336 1.5 jdolecek }
337 1.26 chs mutex_exit(t->p_lock);
338 1.26 chs break;
339 1.5 jdolecek
340 1.25 rmind case LINUX_PTRACE_POKEUSR:
341 1.5 jdolecek /* we only support setting debugregs for now */
342 1.5 jdolecek addr = SCARG(uap, addr);
343 1.26 chs if (addr >= LUSR_OFF(u_debugreg) &&
344 1.26 chs addr <= LUSR_OFF(u_debugreg_end)) {
345 1.5 jdolecek int off = (addr - LUSR_OFF(u_debugreg)) / sizeof(int);
346 1.5 jdolecek int data = SCARG(uap, data);
347 1.5 jdolecek
348 1.5 jdolecek /* only do this for Linux processes */
349 1.25 rmind if (t->p_emul != &emul_linux) {
350 1.26 chs mutex_exit(t->p_lock);
351 1.26 chs return EINVAL;
352 1.25 rmind }
353 1.26 chs led = lt->l_emuldata;
354 1.26 chs led->led_debugreg[off] = data;
355 1.5 jdolecek }
356 1.26 chs mutex_exit(t->p_lock);
357 1.26 chs break;
358 1.26 chs
359 1.5 jdolecek default:
360 1.25 rmind mutex_exit(t->p_lock);
361 1.5 jdolecek break;
362 1.1 tron }
363 1.25 rmind out:
364 1.5 jdolecek if (regs)
365 1.25 rmind kmem_free(regs, sizeof(*regs));
366 1.25 rmind if (linux_regs)
367 1.25 rmind kmem_free(linux_regs, sizeof(*linux_regs));
368 1.5 jdolecek if (fpregs)
369 1.25 rmind kmem_free(fpregs, sizeof(*fpregs));
370 1.5 jdolecek if (linux_fpregs)
371 1.25 rmind kmem_free(linux_fpregs, sizeof(*linux_fpregs));
372 1.25 rmind
373 1.25 rmind return error;
374 1.1 tron }
375