Path: blob/main/sys/i386/linux/linux_ptrace_machdep.c
39483 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2001 Alexander Kabaev4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include "opt_cpu.h"2930#include <sys/param.h>31#include <sys/systm.h>32#include <sys/lock.h>33#include <sys/mutex.h>34#include <sys/proc.h>35#include <sys/ptrace.h>36#include <sys/syscallsubr.h>3738#include <machine/md_var.h>39#include <machine/pcb.h>4041#include <i386/linux/linux.h>42#include <i386/linux/linux_proto.h>43#include <compat/linux/linux_signal.h>4445/*46* Linux ptrace requests numbers. Mostly identical to FreeBSD,47* except for MD ones and PT_ATTACH/PT_DETACH.48*/49#define PTRACE_TRACEME 050#define PTRACE_PEEKTEXT 151#define PTRACE_PEEKDATA 252#define PTRACE_PEEKUSR 353#define PTRACE_POKETEXT 454#define PTRACE_POKEDATA 555#define PTRACE_POKEUSR 656#define PTRACE_CONT 757#define PTRACE_KILL 858#define PTRACE_SINGLESTEP 95960#define PTRACE_ATTACH 1661#define PTRACE_DETACH 176263#define LINUX_PTRACE_SYSCALL 246465#define PTRACE_GETREGS 1266#define PTRACE_SETREGS 1367#define PTRACE_GETFPREGS 1468#define PTRACE_SETFPREGS 1569#define PTRACE_GETFPXREGS 1870#define PTRACE_SETFPXREGS 197172#define PTRACE_SETOPTIONS 217374/*75* Linux keeps debug registers at the following76* offset in the user struct77*/78#define LINUX_DBREG_OFFSET 25279#define LINUX_DBREG_SIZE (8*sizeof(l_int))8081static __inline int82map_signum(int signum)83{8485signum = linux_to_bsd_signal(signum);86return ((signum == SIGSTOP)? 0 : signum);87}8889struct linux_pt_reg {90l_long ebx;91l_long ecx;92l_long edx;93l_long esi;94l_long edi;95l_long ebp;96l_long eax;97l_int xds;98l_int xes;99l_int xfs;100l_int xgs;101l_long orig_eax;102l_long eip;103l_int xcs;104l_long eflags;105l_long esp;106l_int xss;107};108109/*110* Translate i386 ptrace registers between Linux and FreeBSD formats.111* The translation is pretty straighforward, for all registers, but112* orig_eax on Linux side and r_trapno and r_err in FreeBSD113*/114static void115map_regs_to_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)116{117linux_r->ebx = bsd_r->r_ebx;118linux_r->ecx = bsd_r->r_ecx;119linux_r->edx = bsd_r->r_edx;120linux_r->esi = bsd_r->r_esi;121linux_r->edi = bsd_r->r_edi;122linux_r->ebp = bsd_r->r_ebp;123linux_r->eax = bsd_r->r_eax;124linux_r->xds = bsd_r->r_ds;125linux_r->xes = bsd_r->r_es;126linux_r->xfs = bsd_r->r_fs;127linux_r->xgs = bsd_r->r_gs;128linux_r->orig_eax = bsd_r->r_eax;129linux_r->eip = bsd_r->r_eip;130linux_r->xcs = bsd_r->r_cs;131linux_r->eflags = bsd_r->r_eflags;132linux_r->esp = bsd_r->r_esp;133linux_r->xss = bsd_r->r_ss;134}135136static void137map_regs_from_linux(struct reg *bsd_r, struct linux_pt_reg *linux_r)138{139bsd_r->r_ebx = linux_r->ebx;140bsd_r->r_ecx = linux_r->ecx;141bsd_r->r_edx = linux_r->edx;142bsd_r->r_esi = linux_r->esi;143bsd_r->r_edi = linux_r->edi;144bsd_r->r_ebp = linux_r->ebp;145bsd_r->r_eax = linux_r->eax;146bsd_r->r_ds = linux_r->xds;147bsd_r->r_es = linux_r->xes;148bsd_r->r_fs = linux_r->xfs;149bsd_r->r_gs = linux_r->xgs;150bsd_r->r_eip = linux_r->eip;151bsd_r->r_cs = linux_r->xcs;152bsd_r->r_eflags = linux_r->eflags;153bsd_r->r_esp = linux_r->esp;154bsd_r->r_ss = linux_r->xss;155}156157struct linux_pt_fpreg {158l_long cwd;159l_long swd;160l_long twd;161l_long fip;162l_long fcs;163l_long foo;164l_long fos;165l_long st_space[2*10];166};167168static void169map_fpregs_to_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)170{171linux_r->cwd = bsd_r->fpr_env[0];172linux_r->swd = bsd_r->fpr_env[1];173linux_r->twd = bsd_r->fpr_env[2];174linux_r->fip = bsd_r->fpr_env[3];175linux_r->fcs = bsd_r->fpr_env[4];176linux_r->foo = bsd_r->fpr_env[5];177linux_r->fos = bsd_r->fpr_env[6];178bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(linux_r->st_space));179}180181static void182map_fpregs_from_linux(struct fpreg *bsd_r, struct linux_pt_fpreg *linux_r)183{184bsd_r->fpr_env[0] = linux_r->cwd;185bsd_r->fpr_env[1] = linux_r->swd;186bsd_r->fpr_env[2] = linux_r->twd;187bsd_r->fpr_env[3] = linux_r->fip;188bsd_r->fpr_env[4] = linux_r->fcs;189bsd_r->fpr_env[5] = linux_r->foo;190bsd_r->fpr_env[6] = linux_r->fos;191bcopy(bsd_r->fpr_acc, linux_r->st_space, sizeof(bsd_r->fpr_acc));192}193194struct linux_pt_fpxreg {195l_ushort cwd;196l_ushort swd;197l_ushort twd;198l_ushort fop;199l_long fip;200l_long fcs;201l_long foo;202l_long fos;203l_long mxcsr;204l_long reserved;205l_long st_space[32];206l_long xmm_space[32];207l_long padding[56];208};209210static int211linux_proc_read_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)212{213214PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);215if (cpu_fxsr == 0)216return (EIO);217bcopy(&get_pcb_user_save_td(td)->sv_xmm, fpxregs, sizeof(*fpxregs));218return (0);219}220221static int222linux_proc_write_fpxregs(struct thread *td, struct linux_pt_fpxreg *fpxregs)223{224225PROC_LOCK_ASSERT(td->td_proc, MA_OWNED);226if (cpu_fxsr == 0)227return (EIO);228bcopy(fpxregs, &get_pcb_user_save_td(td)->sv_xmm, sizeof(*fpxregs));229return (0);230}231232int233linux_ptrace(struct thread *td, struct linux_ptrace_args *uap)234{235union {236struct linux_pt_reg reg;237struct linux_pt_fpreg fpreg;238struct linux_pt_fpxreg fpxreg;239} r;240union {241struct reg bsd_reg;242struct fpreg bsd_fpreg;243struct dbreg bsd_dbreg;244} u;245void *addr;246pid_t pid;247int error, req;248249error = 0;250251/* by default, just copy data intact */252req = uap->req;253pid = (pid_t)uap->pid;254addr = (void *)uap->addr;255256switch (req) {257case PTRACE_TRACEME:258case PTRACE_POKETEXT:259case PTRACE_POKEDATA:260case PTRACE_KILL:261error = kern_ptrace(td, req, pid, addr, uap->data);262break;263case PTRACE_PEEKTEXT:264case PTRACE_PEEKDATA: {265/* need to preserve return value */266int rval = td->td_retval[0];267error = kern_ptrace(td, req, pid, addr, 0);268if (error == 0)269error = copyout(td->td_retval, (void *)uap->data,270sizeof(l_int));271td->td_retval[0] = rval;272break;273}274case PTRACE_DETACH:275error = kern_ptrace(td, PT_DETACH, pid, (void *)1,276map_signum(uap->data));277break;278case PTRACE_SINGLESTEP:279case PTRACE_CONT:280error = kern_ptrace(td, req, pid, (void *)1,281map_signum(uap->data));282break;283case PTRACE_ATTACH:284error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data);285break;286case PTRACE_GETREGS:287/* Linux is using data where FreeBSD is using addr */288error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);289if (error == 0) {290map_regs_to_linux(&u.bsd_reg, &r.reg);291error = copyout(&r.reg, (void *)uap->data,292sizeof(r.reg));293}294break;295case PTRACE_SETREGS:296/* Linux is using data where FreeBSD is using addr */297error = copyin((void *)uap->data, &r.reg, sizeof(r.reg));298if (error == 0) {299map_regs_from_linux(&u.bsd_reg, &r.reg);300error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);301}302break;303case PTRACE_GETFPREGS:304/* Linux is using data where FreeBSD is using addr */305error = kern_ptrace(td, PT_GETFPREGS, pid, &u.bsd_fpreg, 0);306if (error == 0) {307map_fpregs_to_linux(&u.bsd_fpreg, &r.fpreg);308error = copyout(&r.fpreg, (void *)uap->data,309sizeof(r.fpreg));310}311break;312case PTRACE_SETFPREGS:313/* Linux is using data where FreeBSD is using addr */314error = copyin((void *)uap->data, &r.fpreg, sizeof(r.fpreg));315if (error == 0) {316map_fpregs_from_linux(&u.bsd_fpreg, &r.fpreg);317error = kern_ptrace(td, PT_SETFPREGS, pid,318&u.bsd_fpreg, 0);319}320break;321case PTRACE_SETFPXREGS:322error = copyin((void *)uap->data, &r.fpxreg, sizeof(r.fpxreg));323if (error)324break;325/* FALL THROUGH */326case PTRACE_GETFPXREGS: {327struct proc *p;328struct thread *td2;329330if (sizeof(struct linux_pt_fpxreg) != sizeof(struct savexmm)) {331static int once = 0;332if (!once) {333printf("linux: savexmm != linux_pt_fpxreg\n");334once = 1;335}336error = EIO;337break;338}339340if ((p = pfind(uap->pid)) == NULL) {341error = ESRCH;342break;343}344345/* Exiting processes can't be debugged. */346if ((p->p_flag & P_WEXIT) != 0) {347error = ESRCH;348goto fail;349}350351if ((error = p_candebug(td, p)) != 0)352goto fail;353354/* System processes can't be debugged. */355if ((p->p_flag & P_SYSTEM) != 0) {356error = EINVAL;357goto fail;358}359360/* not being traced... */361if ((p->p_flag & P_TRACED) == 0) {362error = EPERM;363goto fail;364}365366/* not being traced by YOU */367if (p->p_pptr != td->td_proc) {368error = EBUSY;369goto fail;370}371372/* not currently stopped */373if (!P_SHOULDSTOP(p) || (p->p_flag & P_WAITED) == 0) {374error = EBUSY;375goto fail;376}377378if (req == PTRACE_GETFPXREGS) {379_PHOLD(p); /* may block */380td2 = FIRST_THREAD_IN_PROC(p);381error = linux_proc_read_fpxregs(td2, &r.fpxreg);382_PRELE(p);383PROC_UNLOCK(p);384if (error == 0)385error = copyout(&r.fpxreg, (void *)uap->data,386sizeof(r.fpxreg));387} else {388/* clear dangerous bits exactly as Linux does*/389r.fpxreg.mxcsr &= 0xffbf;390_PHOLD(p); /* may block */391td2 = FIRST_THREAD_IN_PROC(p);392error = linux_proc_write_fpxregs(td2, &r.fpxreg);393_PRELE(p);394PROC_UNLOCK(p);395}396break;397398fail:399PROC_UNLOCK(p);400break;401}402case PTRACE_PEEKUSR:403case PTRACE_POKEUSR: {404error = EIO;405406/* check addr for alignment */407if (uap->addr < 0 || uap->addr & (sizeof(l_int) - 1))408break;409/*410* Allow Linux programs to access register values in411* user struct. We simulate this through PT_GET/SETREGS412* as necessary.413*/414if (uap->addr < sizeof(struct linux_pt_reg)) {415error = kern_ptrace(td, PT_GETREGS, pid, &u.bsd_reg, 0);416if (error != 0)417break;418419map_regs_to_linux(&u.bsd_reg, &r.reg);420if (req == PTRACE_PEEKUSR) {421error = copyout((char *)&r.reg + uap->addr,422(void *)uap->data, sizeof(l_int));423break;424}425426*(l_int *)((char *)&r.reg + uap->addr) =427(l_int)uap->data;428429map_regs_from_linux(&u.bsd_reg, &r.reg);430error = kern_ptrace(td, PT_SETREGS, pid, &u.bsd_reg, 0);431}432433/*434* Simulate debug registers access435*/436if (uap->addr >= LINUX_DBREG_OFFSET &&437uap->addr <= LINUX_DBREG_OFFSET + LINUX_DBREG_SIZE) {438error = kern_ptrace(td, PT_GETDBREGS, pid, &u.bsd_dbreg,4390);440if (error != 0)441break;442443uap->addr -= LINUX_DBREG_OFFSET;444if (req == PTRACE_PEEKUSR) {445error = copyout((char *)&u.bsd_dbreg +446uap->addr, (void *)uap->data,447sizeof(l_int));448break;449}450451*(l_int *)((char *)&u.bsd_dbreg + uap->addr) =452uap->data;453error = kern_ptrace(td, PT_SETDBREGS, pid,454&u.bsd_dbreg, 0);455}456457break;458}459case LINUX_PTRACE_SYSCALL:460/* fall through */461default:462printf("linux: ptrace(%u, ...) not implemented\n",463(unsigned int)uap->req);464error = EINVAL;465break;466}467468return (error);469}470471472