diff --git a/sys/amd64/linux/linux.h b/sys/amd64/linux/linux.h index 16fe3793eae7..920ca98ce01b 100644 --- a/sys/amd64/linux/linux.h +++ b/sys/amd64/linux/linux.h @@ -1,468 +1,472 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1994-1996 Søren Schmidt * All rights reserved. * Copyright (c) 2013 Dmitry Chagin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _AMD64_LINUX_H_ #define _AMD64_LINUX_H_ #include #include #include #define LINUX_LEGACY_SYSCALLS #define LINUX_DTRACE linuxulator /* * Provide a separate set of types for the Linux types. */ typedef int32_t l_int; typedef int64_t l_long; typedef int16_t l_short; typedef uint32_t l_uint; typedef uint64_t l_ulong; typedef uint16_t l_ushort; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_ulong l_dev_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_long l_loff_t; typedef l_uint l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_ulong l_size_t; typedef l_long l_ssize_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_int l_timer_t; typedef l_int l_mqd_t; typedef l_size_t l_socklen_t; typedef l_ulong l_fd_mask; typedef struct { l_int val[2]; } l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* * Miscellaneous */ #define LINUX_AT_COUNT 20 /* Count of used aux entry types. */ struct l___sysctl_args { l_uintptr_t name; l_int nlen; l_uintptr_t oldval; l_uintptr_t oldlenp; l_uintptr_t newval; l_size_t newlen; l_ulong __spare[4]; }; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; }; /* * stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; }; struct l_newstat { l_dev_t st_dev; l_ino_t st_ino; l_ulong st_nlink; l_uint st_mode; l_uid_t st_uid; l_gid_t st_gid; l_uint __st_pad1; l_dev_t st_rdev; l_off_t st_size; l_long st_blksize; l_long st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_long __unused1; l_long __unused2; l_long __unused3; }; /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 #define LINUX_SA_NOMASK 0x40000000 #define LINUX_SA_ONESHOT 0x80000000 /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 /* sigaltstack */ #define LINUX_MINSIGSTKSZ 2048 typedef void (*l_handler_t)(l_int); typedef struct { l_handler_t lsa_handler; l_ulong lsa_flags; l_uintptr_t lsa_restorer; l_sigset_t lsa_mask; } l_sigaction_t; typedef struct { l_uintptr_t ss_sp; l_int ss_flags; l_size_t ss_size; } l_stack_t; struct l_fpstate { u_int16_t cwd; u_int16_t swd; u_int16_t twd; u_int16_t fop; u_int64_t rip; u_int64_t rdp; u_int32_t mxcsr; u_int32_t mxcsr_mask; u_int32_t st_space[32]; u_int32_t xmm_space[64]; u_int32_t reserved2[24]; }; struct l_sigcontext { l_ulong sc_r8; l_ulong sc_r9; l_ulong sc_r10; l_ulong sc_r11; l_ulong sc_r12; l_ulong sc_r13; l_ulong sc_r14; l_ulong sc_r15; l_ulong sc_rdi; l_ulong sc_rsi; l_ulong sc_rbp; l_ulong sc_rbx; l_ulong sc_rdx; l_ulong sc_rax; l_ulong sc_rcx; l_ulong sc_rsp; l_ulong sc_rip; l_ulong sc_rflags; l_ushort sc_cs; l_ushort sc_gs; l_ushort sc_fs; l_ushort sc___pad0; l_ulong sc_err; l_ulong sc_trapno; l_sigset_t sc_mask; l_ulong sc_cr2; struct l_fpstate *sc_fpstate; l_ulong sc_reserved1[8]; }; struct l_ucontext { l_ulong uc_flags; l_uintptr_t uc_link; l_stack_t uc_stack; struct l_sigcontext uc_mcontext; l_sigset_t uc_sigmask; }; #define LINUX_SI_PREAMBLE_SIZE (4 * sizeof(int)) #define LINUX_SI_MAX_SIZE 128 #define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE - \ LINUX_SI_PREAMBLE_SIZE) / sizeof(l_int)) typedef union l_sigval { l_int sival_int; l_uintptr_t sival_ptr; } l_sigval_t; typedef struct l_siginfo { l_int lsi_signo; l_int lsi_errno; l_int lsi_code; union { l_int _pad[LINUX_SI_PAD_SIZE]; struct { l_pid_t _pid; l_uid_t _uid; } _kill; struct { l_timer_t _tid; l_int _overrun; char _pad[sizeof(l_uid_t) - sizeof(int)]; union l_sigval _sigval; l_uint _sys_private; } _timer; struct { l_pid_t _pid; /* sender's pid */ l_uid_t _uid; /* sender's uid */ union l_sigval _sigval; } _rt; struct { l_pid_t _pid; /* which child */ l_uid_t _uid; /* sender's uid */ l_int _status; /* exit code */ l_clock_t _utime; l_clock_t _stime; } _sigchld; struct { l_uintptr_t _addr; /* Faulting insn/memory ref. */ } _sigfault; struct { l_long _band; /* POLL_IN,POLL_OUT,POLL_MSG */ l_int _fd; } _sigpoll; } _sifields; } l_siginfo_t; #define lsi_pid _sifields._kill._pid #define lsi_uid _sifields._kill._uid #define lsi_tid _sifields._timer._tid #define lsi_overrun _sifields._timer._overrun #define lsi_sys_private _sifields._timer._sys_private #define lsi_status _sifields._sigchld._status #define lsi_utime _sifields._sigchld._utime #define lsi_stime _sifields._sigchld._stime #define lsi_value _sifields._rt._sigval #define lsi_int _sifields._rt._sigval.sival_int #define lsi_ptr _sifields._rt._sigval.sival_ptr #define lsi_addr _sifields._sigfault._addr #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd /* * We make the stack look like Linux expects it when calling a signal * handler, but use the BSD way of calling the handler and sigreturn(). * This means that we need to pass the pointer to the handler too. * It is appended to the frame to not interfere with the rest of it. */ struct l_rt_sigframe { struct l_ucontext sf_sc; struct l_siginfo sf_si; l_handler_t sf_handler; }; /* * mount flags */ #define LINUX_MS_RDONLY 0x0001 #define LINUX_MS_NOSUID 0x0002 #define LINUX_MS_NODEV 0x0004 #define LINUX_MS_NOEXEC 0x0008 #define LINUX_MS_REMOUNT 0x0020 /* * SystemV IPC defines */ #define LINUX_IPC_RMID 0 #define LINUX_IPC_SET 1 #define LINUX_IPC_STAT 2 #define LINUX_IPC_INFO 3 #define LINUX_SHM_LOCK 11 #define LINUX_SHM_UNLOCK 12 #define LINUX_SHM_STAT 13 #define LINUX_SHM_INFO 14 #define LINUX_SHM_RDONLY 0x1000 #define LINUX_SHM_RND 0x2000 #define LINUX_SHM_REMAP 0x4000 /* semctl commands */ #define LINUX_GETPID 11 #define LINUX_GETVAL 12 #define LINUX_GETALL 13 #define LINUX_GETNCNT 14 #define LINUX_GETZCNT 15 #define LINUX_SETVAL 16 #define LINUX_SETALL 17 #define LINUX_SEM_STAT 18 #define LINUX_SEM_INFO 19 union l_semun { l_int val; l_uintptr_t buf; l_uintptr_t array; l_uintptr_t __buf; l_uintptr_t __pad; }; struct l_ifmap { l_ulong mem_start; l_ulong mem_end; l_ushort base_addr; u_char irq; u_char dma; u_char port; /* 3 bytes spare */ }; struct l_ifreq { union { char ifrn_name[LINUX_IFNAMSIZ]; } ifr_ifrn; union { struct l_sockaddr ifru_addr; struct l_sockaddr ifru_dstaddr; struct l_sockaddr ifru_broadaddr; struct l_sockaddr ifru_netmask; struct l_sockaddr ifru_hwaddr; l_short ifru_flags[1]; l_int ifru_ivalue; l_int ifru_mtu; struct l_ifmap ifru_map; char ifru_slave[LINUX_IFNAMSIZ]; l_uintptr_t ifru_data; } ifr_ifru; }; #define ifr_name ifr_ifrn.ifrn_name /* Interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ struct l_ifconf { int ifc_len; union { l_uintptr_t ifcu_buf; l_uintptr_t ifcu_req; } ifc_ifcu; }; #define ifc_buf ifc_ifcu.ifcu_buf #define ifc_req ifc_ifcu.ifcu_req #define LINUX_ARCH_SET_GS 0x1001 #define LINUX_ARCH_SET_FS 0x1002 #define LINUX_ARCH_GET_FS 0x1003 #define LINUX_ARCH_GET_GS 0x1004 #define LINUX_ARCH_CET_STATUS 0x3001 #define linux_copyout_rusage(r, u) copyout(r, u, sizeof(*r)) /* robust futexes */ struct linux_robust_list { l_uintptr_t next; }; struct linux_robust_list_head { struct linux_robust_list list; l_long futex_offset; l_uintptr_t pending_list; }; /* This corresponds to 'struct user_regs_struct' in Linux. */ struct linux_pt_regset { l_ulong r15; l_ulong r14; l_ulong r13; l_ulong r12; l_ulong rbp; l_ulong rbx; l_ulong r11; l_ulong r10; l_ulong r9; l_ulong r8; l_ulong rax; l_ulong rcx; l_ulong rdx; l_ulong rsi; l_ulong rdi; l_ulong orig_rax; l_ulong rip; l_ulong cs; l_ulong eflags; l_ulong rsp; l_ulong ss; l_ulong fs_base; l_ulong gs_base; l_ulong ds; l_ulong es; l_ulong fs; l_ulong gs; }; struct reg; +struct syscall_info; void bsd_to_linux_regset(const struct reg *b_reg, struct linux_pt_regset *l_regset); void linux_to_bsd_regset(struct reg *b_reg, const struct linux_pt_regset *l_regset); - +void linux_ptrace_get_syscall_info_machdep(const struct reg *reg, + struct syscall_info *si); +int linux_ptrace_getregs_machdep(struct thread *td, pid_t pid, + struct linux_pt_regset *l_regset); #endif /* !_AMD64_LINUX_H_ */ diff --git a/sys/amd64/linux/linux_machdep.c b/sys/amd64/linux/linux_machdep.c index e2346f68da3a..67429a1c049f 100644 --- a/sys/amd64/linux/linux_machdep.c +++ b/sys/amd64/linux/linux_machdep.c @@ -1,363 +1,414 @@ /*- * Copyright (c) 2004 Tim J. Robbins * Copyright (c) 2002 Doug Rabson * Copyright (c) 2000 Marcel Moolenaar * All rights reserved. * Copyright (c) 2013 Dmitry Chagin * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#define LINUX_ARCH_AMD64 0xc000003e + int linux_execve(struct thread *td, struct linux_execve_args *args) { struct image_args eargs; char *path; int error; LINUX_CTR(execve); if (!LUSECONVPATH(td)) { error = exec_copyin_args(&eargs, args->path, UIO_USERSPACE, args->argp, args->envp); } else { LCONVPATHEXIST(td, args->path, &path); error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, args->argp, args->envp); LFREEPATH(path); } if (error == 0) error = linux_common_execve(td, &eargs); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } int linux_set_upcall(struct thread *td, register_t stack) { if (stack) td->td_frame->tf_rsp = stack; /* * The newly created Linux thread returns * to the user space by the same path that a parent does. */ td->td_frame->tf_rax = 0; return (0); } int linux_mmap2(struct thread *td, struct linux_mmap2_args *args) { return (linux_mmap_common(td, args->addr, args->len, args->prot, args->flags, args->fd, args->pgoff)); } int linux_mprotect(struct thread *td, struct linux_mprotect_args *uap) { return (linux_mprotect_common(td, uap->addr, uap->len, uap->prot)); } int linux_madvise(struct thread *td, struct linux_madvise_args *uap) { return (linux_madvise_common(td, uap->addr, uap->len, uap->behav)); } int linux_iopl(struct thread *td, struct linux_iopl_args *args) { int error; LINUX_CTR(iopl); if (args->level > 3) return (EINVAL); if ((error = priv_check(td, PRIV_IO)) != 0) return (error); if ((error = securelevel_gt(td->td_ucred, 0)) != 0) return (error); td->td_frame->tf_rflags = (td->td_frame->tf_rflags & ~PSL_IOPL) | (args->level * (PSL_IOPL / 3)); return (0); } int linux_pause(struct thread *td, struct linux_pause_args *args) { struct proc *p = td->td_proc; sigset_t sigmask; LINUX_CTR(pause); PROC_LOCK(p); sigmask = td->td_sigmask; PROC_UNLOCK(p); return (kern_sigsuspend(td, sigmask)); } int linux_arch_prctl(struct thread *td, struct linux_arch_prctl_args *args) { unsigned long long cet[3]; struct pcb *pcb; int error; pcb = td->td_pcb; LINUX_CTR2(arch_prctl, "0x%x, %p", args->code, args->addr); switch (args->code) { case LINUX_ARCH_SET_GS: if (args->addr < VM_MAXUSER_ADDRESS) { update_pcb_bases(pcb); pcb->pcb_gsbase = args->addr; td->td_frame->tf_gs = _ugssel; error = 0; } else error = EPERM; break; case LINUX_ARCH_SET_FS: if (args->addr < VM_MAXUSER_ADDRESS) { update_pcb_bases(pcb); pcb->pcb_fsbase = args->addr; td->td_frame->tf_fs = _ufssel; error = 0; } else error = EPERM; break; case LINUX_ARCH_GET_FS: error = copyout(&pcb->pcb_fsbase, PTRIN(args->addr), sizeof(args->addr)); break; case LINUX_ARCH_GET_GS: error = copyout(&pcb->pcb_gsbase, PTRIN(args->addr), sizeof(args->addr)); break; case LINUX_ARCH_CET_STATUS: memset(cet, 0, sizeof(cet)); error = copyout(&cet, PTRIN(args->addr), sizeof(cet)); break; default: linux_msg(td, "unsupported arch_prctl code %#x", args->code); error = EINVAL; } return (error); } int linux_set_cloned_tls(struct thread *td, void *desc) { struct pcb *pcb; if ((uint64_t)desc >= VM_MAXUSER_ADDRESS) return (EPERM); pcb = td->td_pcb; update_pcb_bases(pcb); pcb->pcb_fsbase = (register_t)desc; td->td_frame->tf_fs = _ufssel; return (0); } int futex_xchgl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_xchgl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_xchgl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_xchgl_smap : futex_xchgl_nosmap); } int futex_addl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_addl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_addl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_addl_smap : futex_addl_nosmap); } int futex_orl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_orl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_orl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_orl_smap : futex_orl_nosmap); } int futex_andl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_andl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_andl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_andl_smap : futex_andl_nosmap); } int futex_xorl_nosmap(int oparg, uint32_t *uaddr, int *oldval); int futex_xorl_smap(int oparg, uint32_t *uaddr, int *oldval); DEFINE_IFUNC(, int, futex_xorl, (int, uint32_t *, int *)) { return ((cpu_stdext_feature & CPUID_STDEXT_SMAP) != 0 ? futex_xorl_smap : futex_xorl_nosmap); } void bsd_to_linux_regset(const struct reg *b_reg, struct linux_pt_regset *l_regset) { l_regset->r15 = b_reg->r_r15; l_regset->r14 = b_reg->r_r14; l_regset->r13 = b_reg->r_r13; l_regset->r12 = b_reg->r_r12; l_regset->rbp = b_reg->r_rbp; l_regset->rbx = b_reg->r_rbx; l_regset->r11 = b_reg->r_r11; l_regset->r10 = b_reg->r_r10; l_regset->r9 = b_reg->r_r9; l_regset->r8 = b_reg->r_r8; l_regset->rax = b_reg->r_rax; l_regset->rcx = b_reg->r_rcx; l_regset->rdx = b_reg->r_rdx; l_regset->rsi = b_reg->r_rsi; l_regset->rdi = b_reg->r_rdi; l_regset->orig_rax = b_reg->r_rax; l_regset->rip = b_reg->r_rip; l_regset->cs = b_reg->r_cs; l_regset->eflags = b_reg->r_rflags; l_regset->rsp = b_reg->r_rsp; l_regset->ss = b_reg->r_ss; l_regset->fs_base = 0; l_regset->gs_base = 0; l_regset->ds = b_reg->r_ds; l_regset->es = b_reg->r_es; l_regset->fs = b_reg->r_fs; l_regset->gs = b_reg->r_gs; } void linux_to_bsd_regset(struct reg *b_reg, const struct linux_pt_regset *l_regset) { b_reg->r_r15 = l_regset->r15; b_reg->r_r14 = l_regset->r14; b_reg->r_r13 = l_regset->r13; b_reg->r_r12 = l_regset->r12; b_reg->r_rbp = l_regset->rbp; b_reg->r_rbx = l_regset->rbx; b_reg->r_r11 = l_regset->r11; b_reg->r_r10 = l_regset->r10; b_reg->r_r9 = l_regset->r9; b_reg->r_r8 = l_regset->r8; b_reg->r_rax = l_regset->rax; b_reg->r_rcx = l_regset->rcx; b_reg->r_rdx = l_regset->rdx; b_reg->r_rsi = l_regset->rsi; b_reg->r_rdi = l_regset->rdi; b_reg->r_rax = l_regset->orig_rax; b_reg->r_rip = l_regset->rip; b_reg->r_cs = l_regset->cs; b_reg->r_rflags = l_regset->eflags; b_reg->r_rsp = l_regset->rsp; b_reg->r_ss = l_regset->ss; b_reg->r_ds = l_regset->ds; b_reg->r_es = l_regset->es; b_reg->r_fs = l_regset->fs; b_reg->r_gs = l_regset->gs; } + +void +linux_ptrace_get_syscall_info_machdep(const struct reg *reg, + struct syscall_info *si) +{ + + si->arch = LINUX_ARCH_AMD64; + si->instruction_pointer = reg->r_rip; + si->stack_pointer = reg->r_rsp; +} + +int +linux_ptrace_getregs_machdep(struct thread *td, pid_t pid, + struct linux_pt_regset *l_regset) +{ + struct ptrace_lwpinfo lwpinfo; + struct pcb *pcb; + int error; + + pcb = td->td_pcb; + if (td == curthread) + update_pcb_bases(pcb); + + l_regset->fs_base = pcb->pcb_fsbase; + l_regset->gs_base = pcb->pcb_gsbase; + + error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); + if (error != 0) { + linux_msg(td, "PT_LWPINFO failed with error %d", error); + return (error); + } + if ((lwpinfo.pl_flags & PL_FLAG_SCE) != 0) { + /* + * Undo the mangling done in exception.S:fast_syscall_common(). + */ + l_regset->r10 = l_regset->rcx; + } + if ((lwpinfo.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) != 0) { + /* + * In Linux, the syscall number - passed to the syscall + * as rax - is preserved in orig_rax; rax gets overwritten + * with syscall return value. + */ + l_regset->orig_rax = lwpinfo.pl_syscall_code; + } + + return (0); +} diff --git a/sys/arm64/linux/linux.h b/sys/arm64/linux/linux.h index 05e5bd189b36..d43795a71a83 100644 --- a/sys/arm64/linux/linux.h +++ b/sys/arm64/linux/linux.h @@ -1,330 +1,335 @@ /*- * Copyright (c) 1994-1996 Søren Schmidt * Copyright (c) 2013 Dmitry Chagin * Copyright (c) 2018 Turing Robotic Industries Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * $FreeBSD$ */ #ifndef _ARM64_LINUX_H_ #define _ARM64_LINUX_H_ #include #include #include #define LINUX_DTRACE linuxulator /* Provide a separate set of types for the Linux types */ typedef int32_t l_int; typedef int64_t l_long; typedef int16_t l_short; typedef uint32_t l_uint; typedef uint64_t l_ulong; typedef uint16_t l_ushort; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_ulong l_dev_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; /* XXX */ typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; /* XXX */ typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_long l_loff_t; typedef l_uint l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_ulong l_size_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_int l_timer_t; /* XXX */ typedef l_int l_mqd_t; typedef l_ulong l_fd_mask; typedef struct { l_int val[2]; } l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* Miscellaneous */ #define LINUX_AT_COUNT 20 struct l___sysctl_args { l_uintptr_t name; l_int nlen; l_uintptr_t oldval; l_uintptr_t oldlenp; l_uintptr_t newval; l_uintptr_t newlen; l_ulong __spare[4]; }; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; }; /* stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; }; #define LINUX_O_DIRECTORY 000040000 /* Must be a directory */ #define LINUX_O_NOFOLLOW 000100000 /* Do not follow links */ #define LINUX_O_DIRECT 000200000 /* Direct disk access hint */ #define LINUX_O_LARGEFILE 000400000 struct l_newstat { l_dev_t st_dev; l_ino_t st_ino; l_uint st_mode; l_uint st_nlink; l_uid_t st_uid; l_gid_t st_gid; l_dev_t st_rdev; l_ulong __st_pad1; l_off_t st_size; l_int st_blksize; l_int __st_pad2; l_long st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_uint __unused1; l_uint __unused2; }; /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 /* XXX */ #define LINUX_SA_NOMASK 0x40000000 /* SA_NODEFER */ #define LINUX_SA_ONESHOT 0x80000000 /* SA_RESETHAND */ /* sigprocmask actions */ #define LINUX_SIG_BLOCK 0 #define LINUX_SIG_UNBLOCK 1 #define LINUX_SIG_SETMASK 2 /* sigaltstack */ #define LINUX_MINSIGSTKSZ 2048 /* XXX */ typedef void (*l_handler_t)(l_int); typedef struct { l_handler_t lsa_handler; l_ulong lsa_flags; l_uintptr_t lsa_restorer; l_sigset_t lsa_mask; } l_sigaction_t; /* XXX */ typedef struct { l_uintptr_t ss_sp; l_int ss_flags; l_size_t ss_size; } l_stack_t; #define LINUX_SI_PREAMBLE_SIZE (4 * sizeof(int)) #define LINUX_SI_MAX_SIZE 128 #define LINUX_SI_PAD_SIZE ((LINUX_SI_MAX_SIZE - \ LINUX_SI_PREAMBLE_SIZE) / sizeof(l_int)) typedef union l_sigval { l_int sival_int; l_uintptr_t sival_ptr; } l_sigval_t; typedef struct l_siginfo { l_int lsi_signo; l_int lsi_errno; l_int lsi_code; union { l_int _pad[LINUX_SI_PAD_SIZE]; struct { l_pid_t _pid; l_uid_t _uid; } _kill; struct { l_timer_t _tid; l_int _overrun; char _pad[sizeof(l_uid_t) - sizeof(int)]; union l_sigval _sigval; l_uint _sys_private; } _timer; struct { l_pid_t _pid; /* sender's pid */ l_uid_t _uid; /* sender's uid */ union l_sigval _sigval; } _rt; struct { l_pid_t _pid; /* which child */ l_uid_t _uid; /* sender's uid */ l_int _status; /* exit code */ l_clock_t _utime; l_clock_t _stime; } _sigchld; struct { l_uintptr_t _addr; /* Faulting insn/memory ref. */ } _sigfault; struct { l_long _band; /* POLL_IN,POLL_OUT,POLL_MSG */ l_int _fd; } _sigpoll; } _sifields; } l_siginfo_t; #define lsi_pid _sifields._kill._pid #define lsi_uid _sifields._kill._uid #define lsi_tid _sifields._timer._tid #define lsi_overrun _sifields._timer._overrun #define lsi_sys_private _sifields._timer._sys_private #define lsi_status _sifields._sigchld._status #define lsi_utime _sifields._sigchld._utime #define lsi_stime _sifields._sigchld._stime #define lsi_value _sifields._rt._sigval #define lsi_int _sifields._rt._sigval.sival_int #define lsi_ptr _sifields._rt._sigval.sival_ptr #define lsi_addr _sifields._sigfault._addr #define lsi_band _sifields._sigpoll._band #define lsi_fd _sifields._sigpoll._fd /* * This structure is different from the one used by Linux, * but it doesn't matter - it's not user-accessible. We need * it instead of the native one because of l_siginfo. */ struct l_sigframe { struct l_siginfo sf_si; ucontext_t sf_uc; }; union l_semun { l_int val; l_uintptr_t buf; l_uintptr_t array; l_uintptr_t __buf; l_uintptr_t __pad; }; struct l_ifmap { l_ulong mem_start; l_ulong mem_end; l_ushort base_addr; u_char irq; u_char dma; u_char port; /* 3 bytes spare*/ }; struct l_ifreq { union { char ifrn_name[LINUX_IFNAMSIZ]; } ifr_ifrn; union { struct l_sockaddr ifru_addr; struct l_sockaddr ifru_dstaddr; struct l_sockaddr ifru_broadaddr; struct l_sockaddr ifru_netmask; struct l_sockaddr ifru_hwaddr; l_short ifru_flags[1]; l_int ifru_ivalue; l_int ifru_mtu; struct l_ifmap ifru_map; char ifru_slave[LINUX_IFNAMSIZ]; l_uintptr_t ifru_data; } ifr_ifru; }; #define ifr_name ifr_ifrn.ifrn_name /* Interface name */ #define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */ #define ifr_ifindex ifr_ifru.ifru_ivalue /* Interface index */ #define linux_copyout_rusage(r, u) copyout(r, u, sizeof(*r)) /* robust futexes */ struct linux_robust_list { l_uintptr_t next; }; struct linux_robust_list_head { struct linux_robust_list list; l_long futex_offset; l_uintptr_t pending_list; }; struct linux_pt_regset { l_ulong x[31]; l_ulong sp; l_ulong pc; l_ulong cpsr; }; struct reg; +struct syscall_info; void bsd_to_linux_regset(const struct reg *b_reg, struct linux_pt_regset *l_regset); void linux_to_bsd_regset(struct reg *b_reg, const struct linux_pt_regset *l_regset); +void linux_ptrace_get_syscall_info_machdep(const struct reg *reg, + struct syscall_info *si); +int linux_ptrace_getregs_machdep(struct thread *td, pid_t pid, + struct linux_pt_regset *l_regset); #endif /* _ARM64_LINUX_H_ */ diff --git a/sys/arm64/linux/linux_machdep.c b/sys/arm64/linux/linux_machdep.c index 746c93bb86c8..317807ba2e45 100644 --- a/sys/arm64/linux/linux_machdep.c +++ b/sys/arm64/linux/linux_machdep.c @@ -1,162 +1,184 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Turing Robotic Industries Inc. * Copyright (c) 2000 Marcel Moolenaar * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include +#define LINUX_ARCH_AARCH64 0xc00000b7 + /* DTrace init */ LIN_SDT_PROVIDER_DECLARE(LINUX_DTRACE); /* DTrace probes */ LIN_SDT_PROBE_DEFINE0(machdep, linux_mmap2, todo); /* * LINUXTODO: deduplicate; linux_execve is common across archs, except that on * amd64 compat linuxulator it calls freebsd32_exec_copyin_args. */ int linux_execve(struct thread *td, struct linux_execve_args *uap) { struct image_args eargs; char *path; int error; if (!LUSECONVPATH(td)) { error = exec_copyin_args(&eargs, uap->path, UIO_USERSPACE, uap->argp, uap->envp); } else { LCONVPATHEXIST(td, uap->path, &path); error = exec_copyin_args(&eargs, path, UIO_SYSSPACE, uap->argp, uap->envp); LFREEPATH(path); } if (error == 0) error = linux_common_execve(td, &eargs); AUDIT_SYSCALL_EXIT(error == EJUSTRETURN ? 0 : error, td); return (error); } int linux_set_upcall(struct thread *td, register_t stack) { if (stack) td->td_frame->tf_sp = stack; /* * The newly created Linux thread returns * to the user space by the same path that a parent does. */ td->td_frame->tf_x[0] = 0; return (0); } /* LINUXTODO: deduplicate arm64 linux_mmap2 */ int linux_mmap2(struct thread *td, struct linux_mmap2_args *uap) { LIN_SDT_PROBE0(machdep, linux_mmap2, todo); return (linux_mmap_common(td, PTROUT(uap->addr), uap->len, uap->prot, uap->flags, uap->fd, uap->pgoff)); } int linux_mprotect(struct thread *td, struct linux_mprotect_args *uap) { return (linux_mprotect_common(td, PTROUT(uap->addr), uap->len, uap->prot)); } int linux_madvise(struct thread *td, struct linux_madvise_args *uap) { return (linux_madvise_common(td, PTROUT(uap->addr), uap->len, uap->behav)); } int linux_set_cloned_tls(struct thread *td, void *desc) { if ((uint64_t)desc >= VM_MAXUSER_ADDRESS) return (EPERM); return (cpu_set_user_tls(td, desc)); } void bsd_to_linux_regset(const struct reg *b_reg, struct linux_pt_regset *l_regset) { KASSERT(sizeof(l_regset->x) == sizeof(b_reg->x) + sizeof(l_ulong), ("%s: size mismatch\n", __func__)); memcpy(l_regset->x, b_reg->x, sizeof(b_reg->x)); l_regset->x[30] = b_reg->lr; l_regset->sp = b_reg->sp; l_regset->pc = b_reg->elr; l_regset->cpsr = b_reg->spsr; } void linux_to_bsd_regset(struct reg *b_reg, const struct linux_pt_regset *l_regset) { KASSERT(sizeof(l_regset->x) == sizeof(b_reg->x) + sizeof(l_ulong), ("%s: size mismatch\n", __func__)); memcpy(b_reg->x, l_regset->x, sizeof(b_reg->x)); b_reg->sp = l_regset->sp; b_reg->elr = l_regset->pc; b_reg->spsr = l_regset->cpsr; } + +void +linux_ptrace_get_syscall_info_machdep(const struct reg *reg, + struct syscall_info *si) +{ + + si->arch = LINUX_ARCH_AARCH64; + si->instruction_pointer = reg->lr; + si->stack_pointer = reg->sp; +} + +int +linux_ptrace_getregs_machdep(struct thread *td __unused, pid_t pid __unused, + struct linux_pt_regset *l_regset __unused) +{ + + return (0); +} + diff --git a/sys/compat/linux/linux_misc.h b/sys/compat/linux/linux_misc.h index bf8f3dd2f0cd..3bfc9843cda2 100644 --- a/sys/compat/linux/linux_misc.h +++ b/sys/compat/linux/linux_misc.h @@ -1,162 +1,184 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2006 Roman Divacky * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _LINUX_MISC_H_ #define _LINUX_MISC_H_ #include #define LINUX_MAX_PID_NS_LEVEL 32 /* bits per mask */ #define LINUX_NFDBITS sizeof(l_fd_mask) * 8 /* * Miscellaneous */ #define LINUX_NAME_MAX 255 #define LINUX_MAX_UTSNAME 65 #define LINUX_CTL_MAXNAME 10 /* defines for prctl */ #define LINUX_PR_SET_PDEATHSIG 1 /* Second arg is a signal. */ #define LINUX_PR_GET_PDEATHSIG 2 /* * Second arg is a ptr to return the * signal. */ #define LINUX_PR_GET_DUMPABLE 3 #define LINUX_PR_SET_DUMPABLE 4 #define LINUX_PR_GET_KEEPCAPS 7 /* Get drop capabilities on setuid */ #define LINUX_PR_SET_KEEPCAPS 8 /* Set drop capabilities on setuid */ #define LINUX_PR_SET_NAME 15 /* Set process name. */ #define LINUX_PR_GET_NAME 16 /* Get process name. */ #define LINUX_PR_GET_SECCOMP 21 #define LINUX_PR_SET_SECCOMP 22 #define LINUX_PR_CAPBSET_READ 23 #define LINUX_PR_SET_NO_NEW_PRIVS 38 #define LINUX_PR_SET_PTRACER 1499557217 #define LINUX_MAX_COMM_LEN 16 /* Maximum length of the process name. */ /* For GET/SET DUMPABLE */ #define LINUX_SUID_DUMP_DISABLE 0 /* Don't coredump setuid processes. */ #define LINUX_SUID_DUMP_USER 1 /* Dump as user of process. */ #define LINUX_SUID_DUMP_ROOT 2 /* Dump as root. */ #define LINUX_MREMAP_MAYMOVE 1 #define LINUX_MREMAP_FIXED 2 #define LINUX_PATH_MAX 4096 /* * Non-standard aux entry types used in Linux ELF binaries. */ #define LINUX_AT_PLATFORM 15 /* String identifying CPU */ #define LINUX_AT_HWCAP 16 /* CPU capabilities */ #define LINUX_AT_CLKTCK 17 /* frequency at which times() increments */ #define LINUX_AT_SECURE 23 /* secure mode boolean */ #define LINUX_AT_BASE_PLATFORM 24 /* string identifying real platform, may * differ from AT_PLATFORM. */ #define LINUX_AT_RANDOM 25 /* address of random bytes */ #define LINUX_AT_HWCAP2 26 /* CPU capabilities, second part */ #define LINUX_AT_EXECFN 31 /* filename of program */ #define LINUX_AT_SYSINFO 32 /* vsyscall */ #define LINUX_AT_SYSINFO_EHDR 33 /* vdso header */ #define LINUX_AT_RANDOM_LEN 16 /* size of random bytes */ /* Linux sets the i387 to extended precision. */ #if defined(__i386__) || defined(__amd64__) #define __LINUX_NPXCW__ 0x37f #endif /* Scheduling policies */ #define LINUX_SCHED_OTHER 0 #define LINUX_SCHED_FIFO 1 #define LINUX_SCHED_RR 2 #define LINUX_MAX_RT_PRIO 100 struct l_new_utsname { char sysname[LINUX_MAX_UTSNAME]; char nodename[LINUX_MAX_UTSNAME]; char release[LINUX_MAX_UTSNAME]; char version[LINUX_MAX_UTSNAME]; char machine[LINUX_MAX_UTSNAME]; char domainname[LINUX_MAX_UTSNAME]; }; #define LINUX_UTIME_NOW 0x3FFFFFFF #define LINUX_UTIME_OMIT 0x3FFFFFFE extern int stclohz; #define LINUX_WNOHANG 0x00000001 #define LINUX_WUNTRACED 0x00000002 #define LINUX_WSTOPPED LINUX_WUNTRACED #define LINUX_WEXITED 0x00000004 #define LINUX_WCONTINUED 0x00000008 #define LINUX_WNOWAIT 0x01000000 #define __WNOTHREAD 0x20000000 #define __WALL 0x40000000 #define __WCLONE 0x80000000 /* Linux waitid idtype */ #define LINUX_P_ALL 0 #define LINUX_P_PID 1 #define LINUX_P_PGID 2 #define LINUX_RLIMIT_LOCKS 10 #define LINUX_RLIMIT_SIGPENDING 11 #define LINUX_RLIMIT_MSGQUEUE 12 #define LINUX_RLIMIT_NICE 13 #define LINUX_RLIMIT_RTPRIO 14 #define LINUX_RLIMIT_RTTIME 15 #define LINUX_RLIM_INFINITY (~0UL) /* Linux getrandom flags */ #define LINUX_GRND_NONBLOCK 0x0001 #define LINUX_GRND_RANDOM 0x0002 /* Linux syslog flags */ #define LINUX_SYSLOG_ACTION_READ_ALL 3 #if defined(__aarch64__) || (defined(__amd64__) && !defined(COMPAT_LINUX32)) int linux_ptrace_status(struct thread *td, int pid, int status); #endif void linux_to_bsd_waitopts(int options, int *bsdopts); struct thread *linux_tdfind(struct thread *, lwpid_t, pid_t); +struct syscall_info { + uint8_t op; + uint32_t arch; + uint64_t instruction_pointer; + uint64_t stack_pointer; + union { + struct { + uint64_t nr; + uint64_t args[6]; + } entry; + struct { + int64_t rval; + uint8_t is_error; + } exit; + struct { + uint64_t nr; + uint64_t args[6]; + uint32_t ret_data; + } seccomp; + }; +}; + #endif /* _LINUX_MISC_H_ */ diff --git a/sys/compat/linux/linux_ptrace.c b/sys/compat/linux/linux_ptrace.c index c6839d573ad3..8831781702b7 100644 --- a/sys/compat/linux/linux_ptrace.c +++ b/sys/compat/linux/linux_ptrace.c @@ -1,716 +1,620 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2017 Edward Tomasz Napierala * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LINUX_PTRACE_TRACEME 0 #define LINUX_PTRACE_PEEKTEXT 1 #define LINUX_PTRACE_PEEKDATA 2 #define LINUX_PTRACE_PEEKUSER 3 #define LINUX_PTRACE_POKETEXT 4 #define LINUX_PTRACE_POKEDATA 5 #define LINUX_PTRACE_POKEUSER 6 #define LINUX_PTRACE_CONT 7 #define LINUX_PTRACE_KILL 8 #define LINUX_PTRACE_SINGLESTEP 9 #define LINUX_PTRACE_GETREGS 12 #define LINUX_PTRACE_SETREGS 13 #define LINUX_PTRACE_GETFPREGS 14 #define LINUX_PTRACE_SETFPREGS 15 #define LINUX_PTRACE_ATTACH 16 #define LINUX_PTRACE_DETACH 17 #define LINUX_PTRACE_SYSCALL 24 #define LINUX_PTRACE_SETOPTIONS 0x4200 #define LINUX_PTRACE_GETEVENTMSG 0x4201 #define LINUX_PTRACE_GETSIGINFO 0x4202 #define LINUX_PTRACE_GETREGSET 0x4204 #define LINUX_PTRACE_SEIZE 0x4206 #define LINUX_PTRACE_GET_SYSCALL_INFO 0x420e #define LINUX_PTRACE_EVENT_EXEC 4 #define LINUX_PTRACE_EVENT_EXIT 6 #define LINUX_PTRACE_O_TRACESYSGOOD 1 #define LINUX_PTRACE_O_TRACEFORK 2 #define LINUX_PTRACE_O_TRACEVFORK 4 #define LINUX_PTRACE_O_TRACECLONE 8 #define LINUX_PTRACE_O_TRACEEXEC 16 #define LINUX_PTRACE_O_TRACEVFORKDONE 32 #define LINUX_PTRACE_O_TRACEEXIT 64 #define LINUX_PTRACE_O_TRACESECCOMP 128 #define LINUX_PTRACE_O_EXITKILL 1048576 #define LINUX_PTRACE_O_SUSPEND_SECCOMP 2097152 #define LINUX_NT_PRSTATUS 0x1 #define LINUX_NT_PRFPREG 0x2 #define LINUX_NT_X86_XSTATE 0x202 #define LINUX_PTRACE_O_MASK (LINUX_PTRACE_O_TRACESYSGOOD | \ LINUX_PTRACE_O_TRACEFORK | LINUX_PTRACE_O_TRACEVFORK | \ LINUX_PTRACE_O_TRACECLONE | LINUX_PTRACE_O_TRACEEXEC | \ LINUX_PTRACE_O_TRACEVFORKDONE | LINUX_PTRACE_O_TRACEEXIT | \ LINUX_PTRACE_O_TRACESECCOMP | LINUX_PTRACE_O_EXITKILL | \ LINUX_PTRACE_O_SUSPEND_SECCOMP) #define LINUX_PTRACE_SYSCALL_INFO_NONE 0 #define LINUX_PTRACE_SYSCALL_INFO_ENTRY 1 #define LINUX_PTRACE_SYSCALL_INFO_EXIT 2 #define LINUX_PTRACE_PEEKUSER_ORIG_RAX 120 #define LINUX_PTRACE_PEEKUSER_RIP 128 #define LINUX_PTRACE_PEEKUSER_CS 136 #define LINUX_PTRACE_PEEKUSER_DS 184 -#define LINUX_ARCH_AMD64 0xc000003e -#define LINUX_ARCH_AARCH64 0xc00000b7 - static int map_signum(int lsig, int *bsigp) { int bsig; if (lsig == 0) { *bsigp = 0; return (0); } if (lsig < 0 || lsig > LINUX_SIGRTMAX) return (EINVAL); bsig = linux_to_bsd_signal(lsig); if (bsig == SIGSTOP) bsig = 0; *bsigp = bsig; return (0); } int linux_ptrace_status(struct thread *td, pid_t pid, int status) { struct ptrace_lwpinfo lwpinfo; struct linux_pemuldata *pem; register_t saved_retval; int error; saved_retval = td->td_retval[0]; error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); td->td_retval[0] = saved_retval; if (error != 0) { linux_msg(td, "PT_LWPINFO failed with error %d", error); return (status); } pem = pem_find(td->td_proc); KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__)); LINUX_PEM_SLOCK(pem); if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) && lwpinfo.pl_flags & PL_FLAG_SCE) status |= (LINUX_SIGTRAP | 0x80) << 8; if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACESYSGOOD) && lwpinfo.pl_flags & PL_FLAG_SCX) { if (lwpinfo.pl_flags & PL_FLAG_EXEC) status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXEC << 8) << 8; else status |= (LINUX_SIGTRAP | 0x80) << 8; } if ((pem->ptrace_flags & LINUX_PTRACE_O_TRACEEXIT) && lwpinfo.pl_flags & PL_FLAG_EXITED) status |= (LINUX_SIGTRAP | LINUX_PTRACE_EVENT_EXIT << 8) << 8; LINUX_PEM_SUNLOCK(pem); return (status); } -struct syscall_info { - uint8_t op; - uint32_t arch; - uint64_t instruction_pointer; - uint64_t stack_pointer; - union { - struct { - uint64_t nr; - uint64_t args[6]; - } entry; - struct { - int64_t rval; - uint8_t is_error; - } exit; - struct { - uint64_t nr; - uint64_t args[6]; - uint32_t ret_data; - } seccomp; - }; -}; - static int linux_ptrace_peek(struct thread *td, pid_t pid, void *addr, void *data) { int error; error = kern_ptrace(td, PT_READ_I, pid, addr, 0); if (error == 0) error = copyout(td->td_retval, data, sizeof(l_int)); else if (error == ENOMEM) error = EIO; td->td_retval[0] = error; return (error); } static int linux_ptrace_peekuser(struct thread *td, pid_t pid, void *addr, void *data) { struct reg b_reg; uint64_t val; int error; error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); if (error != 0) return (error); switch ((uintptr_t)addr) { #ifdef __amd64__ case LINUX_PTRACE_PEEKUSER_ORIG_RAX: val = b_reg.r_rax; break; case LINUX_PTRACE_PEEKUSER_RIP: val = b_reg.r_rip; break; case LINUX_PTRACE_PEEKUSER_CS: val = b_reg.r_cs; break; case LINUX_PTRACE_PEEKUSER_DS: val = b_reg.r_ds; break; #endif /* __amd64__ */ default: linux_msg(td, "PTRACE_PEEKUSER offset %ld not implemented; " "returning EINVAL", (uintptr_t)addr); return (EINVAL); } error = copyout(&val, data, sizeof(val)); td->td_retval[0] = error; return (error); } static int linux_ptrace_pokeuser(struct thread *td, pid_t pid, void *addr, void *data) { linux_msg(td, "PTRACE_POKEUSER not implemented; returning EINVAL"); return (EINVAL); } static int linux_ptrace_setoptions(struct thread *td, pid_t pid, l_ulong data) { struct linux_pemuldata *pem; int mask; mask = 0; if (data & ~LINUX_PTRACE_O_MASK) { linux_msg(td, "unknown ptrace option %lx set; " "returning EINVAL", data & ~LINUX_PTRACE_O_MASK); return (EINVAL); } pem = pem_find(td->td_proc); KASSERT(pem != NULL, ("%s: proc emuldata not found.\n", __func__)); /* * PTRACE_O_EXITKILL is ignored, we do that by default. */ LINUX_PEM_XLOCK(pem); if (data & LINUX_PTRACE_O_TRACESYSGOOD) { pem->ptrace_flags |= LINUX_PTRACE_O_TRACESYSGOOD; } else { pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACESYSGOOD; } LINUX_PEM_XUNLOCK(pem); if (data & LINUX_PTRACE_O_TRACEFORK) mask |= PTRACE_FORK; if (data & LINUX_PTRACE_O_TRACEVFORK) mask |= PTRACE_VFORK; if (data & LINUX_PTRACE_O_TRACECLONE) mask |= PTRACE_VFORK; if (data & LINUX_PTRACE_O_TRACEEXEC) mask |= PTRACE_EXEC; if (data & LINUX_PTRACE_O_TRACEVFORKDONE) mask |= PTRACE_VFORK; /* XXX: Close enough? */ if (data & LINUX_PTRACE_O_TRACEEXIT) { pem->ptrace_flags |= LINUX_PTRACE_O_TRACEEXIT; } else { pem->ptrace_flags &= ~LINUX_PTRACE_O_TRACEEXIT; } return (kern_ptrace(td, PT_SET_EVENT_MASK, pid, &mask, sizeof(mask))); } static int linux_ptrace_geteventmsg(struct thread *td, pid_t pid, l_ulong data) { linux_msg(td, "PTRACE_GETEVENTMSG not implemented; returning EINVAL"); return (EINVAL); } static int linux_ptrace_getsiginfo(struct thread *td, pid_t pid, l_ulong data) { struct ptrace_lwpinfo lwpinfo; l_siginfo_t l_siginfo; int error, sig; error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); if (error != 0) { linux_msg(td, "PT_LWPINFO failed with error %d", error); return (error); } if ((lwpinfo.pl_flags & PL_FLAG_SI) == 0) { error = EINVAL; linux_msg(td, "no PL_FLAG_SI, returning %d", error); return (error); } sig = bsd_to_linux_signal(lwpinfo.pl_siginfo.si_signo); memset(&l_siginfo, 0, sizeof(l_siginfo)); siginfo_to_lsiginfo(&lwpinfo.pl_siginfo, &l_siginfo, sig); error = copyout(&l_siginfo, (void *)data, sizeof(l_siginfo)); return (error); } static int linux_ptrace_getregs(struct thread *td, pid_t pid, void *data) { struct reg b_reg; struct linux_pt_regset l_regset; -#ifdef __amd64__ - struct ptrace_lwpinfo lwpinfo; - struct pcb *pcb; -#endif int error; error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); if (error != 0) return (error); bsd_to_linux_regset(&b_reg, &l_regset); - -#ifdef __amd64__ - pcb = td->td_pcb; - if (td == curthread) - update_pcb_bases(pcb); - - l_regset.fs_base = pcb->pcb_fsbase; - l_regset.gs_base = pcb->pcb_gsbase; - - error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); - if (error != 0) { - linux_msg(td, "PT_LWPINFO failed with error %d", error); + error = linux_ptrace_getregs_machdep(td, pid, &l_regset); + if (error != 0) return (error); - } - if (lwpinfo.pl_flags & PL_FLAG_SCE) { - /* - * Undo the mangling done in exception.S:fast_syscall_common(). - */ - l_regset.r10 = l_regset.rcx; - } - if (lwpinfo.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) { - /* - * In Linux, the syscall number - passed to the syscall - * as rax - is preserved in orig_rax; rax gets overwritten - * with syscall return value. - */ - l_regset.orig_rax = lwpinfo.pl_syscall_code; - } -#endif error = copyout(&l_regset, (void *)data, sizeof(l_regset)); return (error); } static int linux_ptrace_setregs(struct thread *td, pid_t pid, void *data) { struct reg b_reg; struct linux_pt_regset l_regset; int error; error = copyin(data, &l_regset, sizeof(l_regset)); if (error != 0) return (error); linux_to_bsd_regset(&b_reg, &l_regset); error = kern_ptrace(td, PT_SETREGS, pid, &b_reg, 0); return (error); } static int linux_ptrace_getregset_prstatus(struct thread *td, pid_t pid, l_ulong data) { struct reg b_reg; struct linux_pt_regset l_regset; struct iovec iov; -#ifdef __amd64__ - struct ptrace_lwpinfo lwpinfo; - struct pcb *pcb; -#endif size_t len; int error; error = copyin((const void *)data, &iov, sizeof(iov)); if (error != 0) { linux_msg(td, "copyin error %d", error); return (error); } error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); if (error != 0) return (error); bsd_to_linux_regset(&b_reg, &l_regset); - -#ifdef __amd64__ - pcb = td->td_pcb; - if (td == curthread) - update_pcb_bases(pcb); - - l_regset.fs_base = pcb->pcb_fsbase; - l_regset.gs_base = pcb->pcb_gsbase; - - error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); - if (error != 0) { - linux_msg(td, "PT_LWPINFO failed with error %d", error); + error = linux_ptrace_getregs_machdep(td, pid, &l_regset); + if (error != 0) return (error); - } - if (lwpinfo.pl_flags & PL_FLAG_SCE) { - /* - * Undo the mangling done in exception.S:fast_syscall_common(). - */ - l_regset.r10 = l_regset.rcx; - } - - if (lwpinfo.pl_flags & (PL_FLAG_SCE | PL_FLAG_SCX)) { - /* - * In Linux, the syscall number - passed to the syscall - * as rax - is preserved in orig_rax; rax gets overwritten - * with syscall return value. - */ - l_regset.orig_rax = lwpinfo.pl_syscall_code; - } -#endif len = MIN(iov.iov_len, sizeof(l_regset)); error = copyout(&l_regset, (void *)iov.iov_base, len); if (error != 0) { linux_msg(td, "copyout error %d", error); return (error); } iov.iov_len = len; error = copyout(&iov, (void *)data, sizeof(iov)); if (error != 0) { linux_msg(td, "iov copyout error %d", error); return (error); } return (error); } static int linux_ptrace_getregset(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { switch (addr) { case LINUX_NT_PRSTATUS: return (linux_ptrace_getregset_prstatus(td, pid, data)); case LINUX_NT_PRFPREG: linux_msg(td, "PTRAGE_GETREGSET NT_PRFPREG not implemented; " "returning EINVAL"); return (EINVAL); case LINUX_NT_X86_XSTATE: linux_msg(td, "PTRAGE_GETREGSET NT_X86_XSTATE not implemented; " "returning EINVAL"); return (EINVAL); default: linux_msg(td, "PTRACE_GETREGSET request %#lx not implemented; " "returning EINVAL", addr); return (EINVAL); } } static int linux_ptrace_seize(struct thread *td, pid_t pid, l_ulong addr, l_ulong data) { linux_msg(td, "PTRACE_SEIZE not implemented; returning EINVAL"); return (EINVAL); } static int linux_ptrace_get_syscall_info(struct thread *td, pid_t pid, l_ulong len, l_ulong data) { struct ptrace_lwpinfo lwpinfo; struct ptrace_sc_ret sr; struct reg b_reg; struct syscall_info si; int error; error = kern_ptrace(td, PT_LWPINFO, pid, &lwpinfo, sizeof(lwpinfo)); if (error != 0) { linux_msg(td, "PT_LWPINFO failed with error %d", error); return (error); } memset(&si, 0, sizeof(si)); if (lwpinfo.pl_flags & PL_FLAG_SCE) { si.op = LINUX_PTRACE_SYSCALL_INFO_ENTRY; si.entry.nr = lwpinfo.pl_syscall_code; /* * The use of PT_GET_SC_ARGS there is special, * implementation of PT_GET_SC_ARGS for Linux-ABI * callers emulates Linux bug which strace(1) depends * on: at initialization it tests whether ptrace works * by calling close(2), or some other single-argument * syscall, _with six arguments_, and then verifies * whether it can fetch them all using this API; * otherwise it bails out. */ error = kern_ptrace(td, PT_GET_SC_ARGS, pid, &si.entry.args, sizeof(si.entry.args)); if (error != 0) { linux_msg(td, "PT_GET_SC_ARGS failed with error %d", error); return (error); } } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { si.op = LINUX_PTRACE_SYSCALL_INFO_EXIT; error = kern_ptrace(td, PT_GET_SC_RET, pid, &sr, sizeof(sr)); if (error != 0) { linux_msg(td, "PT_GET_SC_RET failed with error %d", error); return (error); } if (sr.sr_error == 0) { si.exit.rval = sr.sr_retval[0]; si.exit.is_error = 0; } else if (sr.sr_error == EJUSTRETURN) { /* * EJUSTRETURN means the actual value to return * has already been put into td_frame; instead * of extracting it and trying to determine whether * it's an error or not just bail out and let * the ptracing process fall back to another method. */ si.op = LINUX_PTRACE_SYSCALL_INFO_NONE; } else if (sr.sr_error == ERESTART) { si.exit.rval = -LINUX_ERESTARTSYS; si.exit.is_error = 1; } else { si.exit.rval = bsd_to_linux_errno(sr.sr_error); si.exit.is_error = 1; } } else { si.op = LINUX_PTRACE_SYSCALL_INFO_NONE; } error = kern_ptrace(td, PT_GETREGS, pid, &b_reg, 0); if (error != 0) return (error); -#if defined(__amd64__) - si.arch = LINUX_ARCH_AMD64; - si.instruction_pointer = b_reg.r_rip; - si.stack_pointer = b_reg.r_rsp; -#elif defined(__aarch64__) - si.arch = LINUX_ARCH_AARCH64; - si.instruction_pointer = b_reg.lr; - si.stack_pointer = b_reg.sp; -#else -#error "unknown architecture" -#endif + linux_ptrace_get_syscall_info_machdep(&b_reg, &si); len = MIN(len, sizeof(si)); error = copyout(&si, (void *)data, len); if (error == 0) td->td_retval[0] = sizeof(si); return (error); } int linux_ptrace(struct thread *td, struct linux_ptrace_args *uap) { void *addr; pid_t pid; int error, sig; if (!allow_ptrace) return (ENOSYS); pid = (pid_t)uap->pid; addr = (void *)uap->addr; switch (uap->req) { case LINUX_PTRACE_TRACEME: error = kern_ptrace(td, PT_TRACE_ME, 0, 0, 0); break; case LINUX_PTRACE_PEEKTEXT: case LINUX_PTRACE_PEEKDATA: error = linux_ptrace_peek(td, pid, addr, (void *)uap->data); if (error != 0) goto out; /* * Linux expects this syscall to read 64 bits, not 32. */ error = linux_ptrace_peek(td, pid, (void *)(uap->addr + 4), (void *)(uap->data + 4)); break; case LINUX_PTRACE_PEEKUSER: error = linux_ptrace_peekuser(td, pid, addr, (void *)uap->data); break; case LINUX_PTRACE_POKETEXT: case LINUX_PTRACE_POKEDATA: error = kern_ptrace(td, PT_WRITE_D, pid, addr, uap->data); if (error != 0) goto out; /* * Linux expects this syscall to write 64 bits, not 32. */ error = kern_ptrace(td, PT_WRITE_D, pid, (void *)(uap->addr + 4), uap->data >> 32); break; case LINUX_PTRACE_POKEUSER: error = linux_ptrace_pokeuser(td, pid, addr, (void *)uap->data); break; case LINUX_PTRACE_CONT: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_CONTINUE, pid, (void *)1, sig); break; case LINUX_PTRACE_KILL: error = kern_ptrace(td, PT_KILL, pid, addr, uap->data); break; case LINUX_PTRACE_SINGLESTEP: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_STEP, pid, (void *)1, sig); break; case LINUX_PTRACE_GETREGS: error = linux_ptrace_getregs(td, pid, (void *)uap->data); break; case LINUX_PTRACE_SETREGS: error = linux_ptrace_setregs(td, pid, (void *)uap->data); break; case LINUX_PTRACE_ATTACH: error = kern_ptrace(td, PT_ATTACH, pid, addr, uap->data); break; case LINUX_PTRACE_DETACH: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_DETACH, pid, (void *)1, sig); break; case LINUX_PTRACE_SYSCALL: error = map_signum(uap->data, &sig); if (error != 0) break; error = kern_ptrace(td, PT_SYSCALL, pid, (void *)1, sig); break; case LINUX_PTRACE_SETOPTIONS: error = linux_ptrace_setoptions(td, pid, uap->data); break; case LINUX_PTRACE_GETEVENTMSG: error = linux_ptrace_geteventmsg(td, pid, uap->data); break; case LINUX_PTRACE_GETSIGINFO: error = linux_ptrace_getsiginfo(td, pid, uap->data); break; case LINUX_PTRACE_GETREGSET: error = linux_ptrace_getregset(td, pid, uap->addr, uap->data); break; case LINUX_PTRACE_SEIZE: error = linux_ptrace_seize(td, pid, uap->addr, uap->data); break; case LINUX_PTRACE_GET_SYSCALL_INFO: error = linux_ptrace_get_syscall_info(td, pid, uap->addr, uap->data); break; default: linux_msg(td, "ptrace(%ld, ...) not implemented; " "returning EINVAL", uap->req); error = EINVAL; break; } out: if (error == EBUSY) error = ESRCH; return (error); }