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