[CRIU] [PATCH 21/22] compel: Wire in ptrace interface
Cyrill Gorcunov
gorcunov at openvz.org
Wed Oct 19 12:21:36 PDT 2016
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
compel/Makefile | 1 +
.../aarch64/src/lib/include/compel/asm/ptrace.c | 40 ++++
.../arch/arm/src/lib/include/compel/asm/ptrace.c | 50 +++++
.../arch/ppc64/src/lib/include/compel/asm/ptrace.c | 248 +++++++++++++++++++++
.../arch/x86/src/lib/include/compel/asm/ptrace.c | 217 ++++++++++++++++++
compel/include/compel/errno.h | 9 +
compel/include/uapi/compel/ptrace.h | 84 +++++++
{criu => compel/src/lib}/ptrace.c | 43 ++--
criu/Makefile.crtools | 1 -
criu/arch/aarch64/crtools.c | 5 +-
criu/arch/aarch64/include/asm/restorer.h | 10 -
criu/arch/arm/crtools.c | 5 +-
criu/arch/arm/include/asm/restorer.h | 10 -
criu/arch/ppc64/crtools.c | 5 +-
criu/arch/ppc64/include/asm/restorer.h | 10 -
criu/arch/x86/crtools.c | 149 +------------
criu/arch/x86/include/asm/dump.h | 4 -
criu/include/ptrace.h | 73 +-----
criu/parasite-syscall.c | 20 --
19 files changed, 687 insertions(+), 297 deletions(-)
create mode 100644 compel/arch/aarch64/src/lib/include/compel/asm/ptrace.c
create mode 100644 compel/arch/arm/src/lib/include/compel/asm/ptrace.c
create mode 100644 compel/arch/ppc64/src/lib/include/compel/asm/ptrace.c
create mode 100644 compel/arch/x86/src/lib/include/compel/asm/ptrace.c
create mode 100644 compel/include/compel/errno.h
create mode 100644 compel/include/uapi/compel/ptrace.h
rename {criu => compel/src/lib}/ptrace.c (70%)
diff --git a/compel/Makefile b/compel/Makefile
index 85b853ea3a39..adf864eb383f 100644
--- a/compel/Makefile
+++ b/compel/Makefile
@@ -27,6 +27,7 @@ lib-y += src/lib/argv.o
lib-y += src/lib/compel.o
lib-y += arch/$(ARCH)/src/lib/cpu.o
lib-y += arch/$(ARCH)/src/lib/sigframe.o
+lib-y += src/lib/ptrace.o
#
# This requires arch specific header to
diff --git a/compel/arch/aarch64/src/lib/include/compel/asm/ptrace.c b/compel/arch/aarch64/src/lib/include/compel/asm/ptrace.c
new file mode 100644
index 000000000000..56b179df2a3c
--- /dev/null
+++ b/compel/arch/aarch64/src/lib/include/compel/asm/ptrace.c
@@ -0,0 +1,40 @@
+/*
+ * Included from the toplevel.
+ */
+
+int ptrace_set_breakpoint(pid_t pid, void *addr) { return 0; }
+int ptrace_flush_breakpoints(pid_t pid) { return 0; }
+bool ptrace_task_compatible(pid_t pid)
+{
+ /*
+ * TODO: Add proper check here
+ */
+ return true;
+}
+
+int compel_get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg)
+{
+ user_fpregs_struct_t fpsimd;
+ struct iovec iov;
+ int ret;
+
+ pr_info("Obtaining GP/FPU registers for %d\n", pid);
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(user_regs_struct_t);
+ if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov))) {
+ pr_perror("Failed to obtain CPU registers for %d", pid);
+ goto err;
+ }
+
+ iov.iov_base = &fpsimd;
+ iov.iov_len = sizeof(fpsimd);
+ if ((ret = ptrace(PTRACE_GETREGSET, pid, NT_FPREGSET, &iov))) {
+ pr_perror("Failed to obtain FPU registers for %d", pid);
+ goto err;
+ }
+
+ ret = save(arg, ®s, &fpsimd);
+err:
+ return ret;
+}
diff --git a/compel/arch/arm/src/lib/include/compel/asm/ptrace.c b/compel/arch/arm/src/lib/include/compel/asm/ptrace.c
new file mode 100644
index 000000000000..8a5cb31ffa28
--- /dev/null
+++ b/compel/arch/arm/src/lib/include/compel/asm/ptrace.c
@@ -0,0 +1,50 @@
+/*
+ * Included from the toplevel.
+ */
+
+#include "uapi/compel/plugins/std/syscall-codes.h"
+
+int ptrace_set_breakpoint(pid_t pid, void *addr) { return 0; }
+int ptrace_flush_breakpoints(pid_t pid) { return 0; }
+bool ptrace_task_compatible(pid_t pid)
+{
+ /*
+ * TODO: Add proper check here
+ */
+ return true;
+}
+
+#define PTRACE_GETVFPREGS 27
+int compel_get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg)
+{
+ user_fpregs_struct_t vfp;
+ int ret = -1;
+
+ pr_info("Obtaining GP/FPU registers for %d\n", pid);
+
+ if (ptrace(PTRACE_GETVFPREGS, pid, NULL, &vfp)) {
+ pr_perror("Can't obtain FPU registers for %d", pid);
+ goto err;
+ }
+
+ /* Did we come from a system call? */
+ if ((int)regs.ARM_ORIG_r0 >= 0) {
+ /* Restart the system call */
+ switch ((long)(int)regs.ARM_r0) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ regs.ARM_r0 = regs.ARM_ORIG_r0;
+ regs.ARM_pc -= 4;
+ break;
+ case -ERESTART_RESTARTBLOCK:
+ regs.ARM_r0 = __NR_restart_syscall;
+ regs.ARM_pc -= 4;
+ break;
+ }
+ }
+
+ ret = save(arg, ®s, &vfp);
+err:
+ return ret;
+}
diff --git a/compel/arch/ppc64/src/lib/include/compel/asm/ptrace.c b/compel/arch/ppc64/src/lib/include/compel/asm/ptrace.c
new file mode 100644
index 000000000000..b55f2b261def
--- /dev/null
+++ b/compel/arch/ppc64/src/lib/include/compel/asm/ptrace.c
@@ -0,0 +1,248 @@
+/*
+ * Included from the toplevel.
+ */
+#include <string.h>
+#include <unistd.h>
+#include <elf.h>
+#include <sys/user.h>
+#include <asm/unistd.h>
+#include <sys/uio.h>
+
+#include "uapi/compel/plugins/std/syscall-codes.h"
+
+int ptrace_set_breakpoint(pid_t pid, void *addr) { return 0; }
+int ptrace_flush_breakpoints(pid_t pid) { return 0; }
+bool ptrace_task_compatible(pid_t pid)
+{
+ /*
+ * TODO: We should detect 32bit task when BE support is done.
+ */
+ return true;
+}
+
+static int get_tm_regs(pid_t pid, user_fpregs_struct_t *fpregs)
+{
+ struct iovec iov;
+
+ pr_debug("Obtaining TM registers\n");
+
+#define TM_REQUIRED 0
+#define TM_OPTIONAL 1
+
+#define PTRACE_GET_TM(s,n,c,u) \
+ do { \
+ iov.iov_base = &s; \
+ iov.iov_len = sizeof(s); \
+ if (ptrace(PTRACE_GETREGSET, pid, c, &iov)) { \
+ if (!u || errno != EIO) { \
+ pr_perror("Couldn't get TM "n"\n"); \
+ pr_err("Your kernel seems to not support the " \
+ "new TM ptrace API (>= 4.8)\n"); \
+ goto out_free; \
+ } \
+ pr_debug("TM "n" is not supported.\n"); \
+ iov.iov_base = NULL; \
+ } \
+ } while(0)
+
+ /* Get special registers */
+ PTRACE_GET_TM(fpregs->tm.tm_spr_regs, "SPR", NT_PPC_TM_SPR, TM_REQUIRED);
+
+ /* Get checkpointed regular registers */
+ PTRACE_GET_TM(fpregs->tm.regs, "GPR", NT_PPC_TM_CGPR, TM_REQUIRED);
+
+ /* Get checkpointed FP registers */
+ PTRACE_GET_TM(fpregs->tm.fpregs, "FPR", NT_PPC_TM_CFPR, TM_OPTIONAL);
+ if (iov.iov_base)
+ fpregs->tm.flags |= USER_FPREGS_FL_FP;
+
+ /* Get checkpointed VMX (Altivec) registers */
+ PTRACE_GET_TM(fpregs->tm.vrregs, "VMX", NT_PPC_TM_CVMX, TM_OPTIONAL);
+ if (iov.iov_base)
+ fpregs->tm.flags |= USER_FPREGS_FL_ALTIVEC;
+
+ /* Get checkpointed VSX registers */
+ PTRACE_GET_TM(fpregs->tm.vsxregs, "VSX", NT_PPC_TM_CVSX, TM_OPTIONAL);
+ if (iov.iov_base)
+ fpregs->tm.flags |= USER_FPREGS_FL_VSX;
+
+ return 0;
+
+out_free:
+ return -1; /* still failing the checkpoint */
+}
+
+/* This is the layout of the POWER7 VSX registers and the way they
+ * overlap with the existing FPR and VMX registers.
+ *
+ * VSR doubleword 0 VSR doubleword 1
+ * ----------------------------------------------------------------
+ * VSR[0] | FPR[0] | |
+ * ----------------------------------------------------------------
+ * VSR[1] | FPR[1] | |
+ * ----------------------------------------------------------------
+ * | ... | |
+ * ----------------------------------------------------------------
+ * VSR[30] | FPR[30] | |
+ * ----------------------------------------------------------------
+ * VSR[31] | FPR[31] | |
+ * ----------------------------------------------------------------
+ * VSR[32] | VR[0] |
+ * ----------------------------------------------------------------
+ * VSR[33] | VR[1] |
+ * ----------------------------------------------------------------
+ * | ... |
+ * ----------------------------------------------------------------
+ * VSR[62] | VR[30] |
+ * ----------------------------------------------------------------
+ * VSR[63] | VR[31] |
+ * ----------------------------------------------------------------
+ *
+ * PTRACE_GETFPREGS returns FPR[0..31] + FPSCR
+ * PTRACE_GETVRREGS returns VR[0..31] + VSCR + VRSAVE
+ * PTRACE_GETVSRREGS returns VSR[0..31]
+ *
+ * PTRACE_GETVSRREGS and PTRACE_GETFPREGS are required since we need
+ * to save FPSCR too.
+ *
+ * There 32 VSX double word registers to save since the 32 first VSX double
+ * word registers are saved through FPR[0..32] and the remaining registers
+ * are saved when saving the Altivec registers VR[0..32].
+ */
+
+static int get_fpu_regs(pid_t pid, user_fpregs_struct_t *fp)
+{
+ if (ptrace(PTRACE_GETFPREGS, pid, 0, (void *)&fp->fpregs) < 0) {
+ pr_perror("Couldn't get floating-point registers");
+ return -1;
+ }
+ fp->flags |= USER_FPREGS_FL_FP;
+
+ return 0;
+}
+
+static int get_altivec_regs(pid_t pid, user_fpregs_struct_t *fp)
+{
+ if (ptrace(PTRACE_GETVRREGS, pid, 0, (void*)&fp->vrregs) < 0) {
+ /* PTRACE_GETVRREGS returns EIO if Altivec is not supported.
+ * This should not happen if msr_vec is set. */
+ if (errno != EIO) {
+ pr_perror("Couldn't get Altivec registers");
+ return -1;
+ }
+ pr_debug("Altivec not supported\n");
+ }
+ else {
+ pr_debug("Obtaining Altivec registers\n");
+ fp->flags |= USER_FPREGS_FL_ALTIVEC;
+ }
+ return 0;
+}
+
+/*
+ * Since the FPR[0-31] is stored in the first double word of VSR[0-31] and
+ * FPR are saved through the FP state, there is no need to save the upper part
+ * of the first 32 VSX registers.
+ * Furthermore, the 32 last VSX registers are also the 32 Altivec registers
+ * already saved, so no need to save them.
+ * As a consequence, only the doubleword 1 of the 32 first VSX registers have
+ * to be saved (the ones are returned by PTRACE_GETVSRREGS).
+ */
+static int get_vsx_regs(pid_t pid, user_fpregs_struct_t *fp)
+{
+ if (ptrace(PTRACE_GETVSRREGS, pid, 0, (void*)fp->vsxregs) < 0) {
+ /*
+ * EIO is returned in the case PTRACE_GETVRREGS is not
+ * supported.
+ */
+ if (errno != EIO) {
+ pr_perror("Couldn't get VSX registers");
+ return -1;
+ }
+ pr_debug("VSX register's dump not supported.\n");
+ }
+ else {
+ pr_debug("Obtaining VSX registers\n");
+ fp->flags |= USER_FPREGS_FL_VSX;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+static int __get_task_regs(pid_t pid, user_regs_struct_t *regs,
+ user_fpregs_struct_t *fpregs)
+{
+ pr_info("Obtaining GP/FPU registers of %d\n", pid);
+
+ /*
+ * This is inspired by kernel function check_syscall_restart in
+ * arch/powerpc/kernel/signal.c
+ */
+#ifndef TRAP
+# define TRAP(r) ((r).trap & ~0xF)
+#endif
+
+ if (TRAP(*regs) == 0x0C00 && regs->ccr & 0x10000000) {
+ /* Restart the system call */
+ switch (regs->gpr[3]) {
+ case ERESTARTNOHAND:
+ case ERESTARTSYS:
+ case ERESTARTNOINTR:
+ regs->gpr[3] = regs->orig_gpr3;
+ regs->nip -= 4;
+ break;
+ case ERESTART_RESTARTBLOCK:
+ regs->gpr[0] = __NR_restart_syscall;
+ regs->nip -= 4;
+ break;
+ }
+ }
+
+ /* Resetting trap since we are now coming from user space. */
+ regs->trap = 0;
+
+ fpregs->flags = 0;
+ /*
+ * Check for Transactional Memory operation in progress.
+ * Until we have support of TM register's state through the ptrace API,
+ * we can't checkpoint process with TM operation in progress (almost
+ * impossible) or suspended (easy to get).
+ */
+ if (MSR_TM_ACTIVE(regs->msr)) {
+ pr_debug("Task %d has %s TM operation at 0x%lx\n",
+ pid,
+ (regs->msr & MSR_TMS) ? "a suspended" : "an active",
+ regs->nip);
+ if (get_tm_regs(pid, fpregs))
+ return -1;
+ fpregs->flags = USER_FPREGS_FL_TM;
+ }
+
+ if (get_fpu_regs(pid, fpregs))
+ return -1;
+
+ if (get_altivec_regs(pid, fpregs))
+ return -1;
+
+ if (fpregs->flags & USER_FPREGS_FL_ALTIVEC) {
+ /*
+ * Save the VSX registers if Altivec registers are supported
+ */
+ if (get_vsx_regs(pid, fpregs))
+ return -1;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+int compel_get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg)
+{
+ user_fpregs_struct_t fpregs;
+ int ret;
+
+ ret = __get_task_regs(pid, ®s, &fpregs);
+ if (ret)
+ return ret;
+
+ return save(arg, ®s, &fpregs);
+}
diff --git a/compel/arch/x86/src/lib/include/compel/asm/ptrace.c b/compel/arch/x86/src/lib/include/compel/asm/ptrace.c
new file mode 100644
index 000000000000..4600f1046fdf
--- /dev/null
+++ b/compel/arch/x86/src/lib/include/compel/asm/ptrace.c
@@ -0,0 +1,217 @@
+/*
+ * Included from the toplevel.
+ */
+
+#include <sys/user.h>
+
+#include "uapi/compel/cpu.h"
+
+#include "compiler.h"
+
+#ifdef LOG_PREFIX
+#undef LOG_PREFIX
+#endif
+
+#define LOG_PREFIX "compel: ptrace: "
+
+/* Copied from the gdb header gdb/nat/x86-dregs.h */
+
+/* Debug registers' indices. */
+#define DR_FIRSTADDR 0
+#define DR_LASTADDR 3
+#define DR_NADDR 4 /* The number of debug address registers */
+#define DR_STATUS 6 /* Index of debug status register (DR6) */
+#define DR_CONTROL 7 /* Index of debug control register (DR7) */
+
+#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */
+#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */
+#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */
+
+/* Locally enable the break/watchpoint in the I'th debug register */
+#define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
+
+int ptrace_set_breakpoint(pid_t pid, void *addr)
+{
+ int ret;
+
+ /* Set a breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_FIRSTADDR]),
+ addr)) {
+ pr_perror("Unable to setup a breakpoint into %d", pid);
+ return -1;
+ }
+
+ /* Enable the breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_CONTROL]),
+ X86_DR_LOCAL_ENABLE(DR_FIRSTADDR))) {
+ pr_perror("Unable to enable the breakpoint for %d", pid);
+ return -1;
+ }
+
+ ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
+ if (ret) {
+ pr_perror("Unable to restart the stopped tracee process %d", pid);
+ return -1;
+ }
+
+ return 1;
+}
+
+int ptrace_flush_breakpoints(pid_t pid)
+{
+ /* Disable the breakpoint */
+ if (ptrace(PTRACE_POKEUSER, pid,
+ offsetof(struct user, u_debugreg[DR_CONTROL]),
+ 0)) {
+ pr_perror("Unable to disable the breakpoint for %d", pid);
+ return -1;
+ }
+
+ return 0;
+}
+
+#define USER32_CS 0x23
+#define USER_CS 0x33
+
+bool ptrace_task_compatible(pid_t pid)
+{
+ user_regs_struct_t r;
+ unsigned long cs;
+
+ if (ptrace_get_regs(pid, &r))
+ return false;
+ if (!user_regs_native(&r)) {
+ pr_err("Non-native registers in task %d\n", pid);
+ return false;
+ }
+
+ errno = 0;
+ /*
+ * Offset of register must be from 64-bit set even for
+ * compatible tasks. Fix this to support native i386 tasks.
+ */
+ cs = ptrace(PTRACE_PEEKUSER, pid, offsetof(user_regs_struct64, cs), 0);
+ if (errno != 0) {
+ pr_perror("Can't get CS register for %d", pid);
+ return false;
+ }
+
+ if (cs != USER_CS && cs != USER32_CS) {
+ pr_err("Can't process task %d with LDT descriptors (cs = %lu)\n",
+ pid, cs);
+ return false;
+ }
+
+ return true;
+}
+
+#define ARCH_HAS_GET_REGS
+
+int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs)
+{
+ struct iovec iov;
+ int ret;
+
+ iov.iov_base = ®s->native;
+ iov.iov_len = sizeof(user_regs_struct64);
+
+ ret = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
+ if (ret == -1) {
+ pr_perror("PTRACE_GETREGSET failed");
+ return -1;
+ }
+
+ if (iov.iov_len == sizeof(regs->native)) {
+ regs->__is_native = NATIVE_MAGIC;
+ return ret;
+ }
+ if (iov.iov_len == sizeof(regs->compat)) {
+ regs->__is_native = COMPAT_MAGIC;
+ return ret;
+ }
+
+ pr_err("PTRACE_GETREGSET read %zu bytes for pid %d, "
+ "but native/compat regs sizes are %zu/%zu bytes",
+ iov.iov_len, pid, sizeof(regs->native), sizeof(regs->compat));
+ return -1;
+}
+
+int ptrace_set_regs(pid_t pid, user_regs_struct_t *regs)
+{
+ struct iovec iov;
+
+ if (user_regs_native(regs)) {
+ iov.iov_base = ®s->native;
+ iov.iov_len = sizeof(user_regs_struct64);
+ } else {
+ iov.iov_base = ®s->compat;
+ iov.iov_len = sizeof(user_regs_struct32);
+ }
+ return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov);
+}
+
+#define get_signed_user_reg(pregs, name) \
+ ((user_regs_native(pregs)) ? \
+ (int64_t)((pregs)->native.name) : \
+ (int32_t)((pregs)->compat.name))
+
+int compel_get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg)
+{
+ user_fpregs_struct_t xsave = { }, *xs = NULL;
+
+ struct iovec iov;
+ int ret = -1;
+
+ pr_info("Obtaining general registers for %d in %s mode\n",
+ pid, user_regs_native(®s) ? "native" : "compat");
+
+ /* Did we come from a system call? */
+ if (get_signed_user_reg(®s, orig_ax) >= 0) {
+ /* Restart the system call */
+ switch (get_signed_user_reg(®s, ax)) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ set_user_reg(®s, ax, get_user_reg(®s, orig_ax));
+ set_user_reg(®s, ip, get_user_reg(®s, ip) - 2);
+ break;
+ case -ERESTART_RESTARTBLOCK:
+ pr_warn("Will restore %d with interrupted system call\n", pid);
+ set_user_reg(®s, ax, -EINTR);
+ break;
+ }
+ }
+
+ if (!cpu_has_feature(X86_FEATURE_FPU))
+ goto out;
+
+ /*
+ * FPU fetched either via fxsave or via xsave,
+ * thus decode it accrodingly.
+ */
+
+ pr_info("Obtaining GP/FPU registers for %d\n", pid);
+
+ if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+ iov.iov_base = &xsave;
+ iov.iov_len = sizeof(xsave);
+
+ if (ptrace(PTRACE_GETREGSET, pid, (unsigned int)NT_X86_XSTATE, &iov) < 0) {
+ pr_perror("Can't obtain FPU registers for %d", pid);
+ goto err;
+ }
+ } else {
+ if (ptrace(PTRACE_GETFPREGS, pid, NULL, &xsave)) {
+ pr_perror("Can't obtain FPU registers for %d", pid);
+ goto err;
+ }
+ }
+
+ xs = &xsave;
+out:
+ ret = save(arg, ®s, xs);
+err:
+ return ret;
+}
diff --git a/compel/include/compel/errno.h b/compel/include/compel/errno.h
new file mode 100644
index 000000000000..28906d647127
--- /dev/null
+++ b/compel/include/compel/errno.h
@@ -0,0 +1,9 @@
+#ifndef COMPEL_ERRNO_H__
+#define COMPEL_ERRNO_H__
+
+#define ERESTARTSYS 512
+#define ERESTARTNOINTR 513
+#define ERESTARTNOHAND 514
+#define ERESTART_RESTARTBLOCK 516
+
+#endif /* COMPEL_ERRNO_H__ */
diff --git a/compel/include/uapi/compel/ptrace.h b/compel/include/uapi/compel/ptrace.h
new file mode 100644
index 000000000000..ee77d577df6c
--- /dev/null
+++ b/compel/include/uapi/compel/ptrace.h
@@ -0,0 +1,84 @@
+#ifndef UAPI_COMPEL_PTRACE_H__
+#define UAPI_COMPEL_PTRACE_H__
+
+#include <linux/types.h>
+#include <sys/ptrace.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <compel/asm/types.h>
+
+/* some constants for ptrace */
+#ifndef PTRACE_SEIZE
+# define PTRACE_SEIZE 0x4206
+#endif
+
+#ifndef PTRACE_O_SUSPEND_SECCOMP
+# define PTRACE_O_SUSPEND_SECCOMP (1 << 21)
+#endif
+
+#ifndef PTRACE_INTERRUPT
+# define PTRACE_INTERRUPT 0x4207
+#endif
+
+#ifndef PTRACE_LISTEN
+# define PTRACE_LISTEN 0x4208
+#endif
+
+#ifndef PTRACE_PEEKSIGINFO
+# define PTRACE_PEEKSIGINFO 0x4209
+
+/* Read signals from a shared (process wide) queue */
+# define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
+#endif
+
+#ifndef PTRACE_GETREGSET
+# define PTRACE_GETREGSET 0x4204
+# define PTRACE_SETREGSET 0x4205
+#endif
+
+#ifndef PTRACE_GETSIGMASK
+# define PTRACE_GETSIGMASK 0x420a
+# define PTRACE_SETSIGMASK 0x420b
+#endif
+
+#ifndef PTRACE_SECCOMP_GET_FILTER
+#define PTRACE_SECCOMP_GET_FILTER 0x420c
+#endif
+
+#define PTRACE_SEIZE_DEVEL 0x80000000
+
+#define PTRACE_EVENT_FORK 1
+#define PTRACE_EVENT_VFORK 2
+#define PTRACE_EVENT_CLONE 3
+#define PTRACE_EVENT_EXEC 4
+#define PTRACE_EVENT_VFORK_DONE 5
+#define PTRACE_EVENT_EXIT 6
+#define PTRACE_EVENT_STOP 128
+
+#define PTRACE_O_TRACESYSGOOD 0x00000001
+#define PTRACE_O_TRACEFORK 0x00000002
+#define PTRACE_O_TRACEVFORK 0x00000004
+#define PTRACE_O_TRACECLONE 0x00000008
+#define PTRACE_O_TRACEEXEC 0x00000010
+#define PTRACE_O_TRACEVFORKDONE 0x00000020
+#define PTRACE_O_TRACEEXIT 0x00000040
+
+#define SI_EVENT(_si_code) (((_si_code) & 0xFFFF) >> 8)
+
+extern int suspend_seccomp(pid_t pid);
+extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
+extern int ptrace_poke_area(pid_t pid, void *src, void *addr, long bytes);
+extern int ptrace_swap_area(pid_t pid, void *dst, void *src, long bytes);
+extern int ptrace_set_breakpoint(pid_t pid, void *addr);
+extern int ptrace_flush_breakpoints(pid_t pid);
+
+extern int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs);
+extern int ptrace_set_regs(pid_t pid, user_regs_struct_t *regs);
+
+extern bool ptrace_task_compatible(pid_t pid);
+
+typedef int (*save_regs_t)(void *arg, user_regs_struct_t *regs, user_fpregs_struct_t *fpregs);
+extern int compel_get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg);
+
+#endif /* UAPI_COMPEL_PTRACE_H__ */
diff --git a/criu/ptrace.c b/compel/src/lib/ptrace.c
similarity index 70%
rename from criu/ptrace.c
rename to compel/src/lib/ptrace.c
index 17171b99ef01..ac2735de84cf 100644
--- a/criu/ptrace.c
+++ b/compel/src/lib/ptrace.c
@@ -1,27 +1,20 @@
-#include <stdlib.h>
#include <stdio.h>
-#include <stdarg.h>
+#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
-#include <stdbool.h>
-#include <limits.h>
-#include <signal.h>
+#include <elf.h>
#include <sys/ptrace.h>
#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/wait.h>
-#include "compiler.h"
+#include "uapi/compel/ptrace.h"
+
+#include "errno.h"
+#include "log.h"
+
+#include "asm/ptrace.c"
#include "asm/types.h"
-#include "util.h"
-#include "ptrace.h"
-#include "pid.h"
-#include "proc_parse.h"
-#include "seccomp.h"
-#include "cr_options.h"
int suspend_seccomp(pid_t pid)
{
@@ -82,3 +75,23 @@ int ptrace_swap_area(pid_t pid, void *dst, void *src, long bytes)
return 0;
}
+
+#ifndef ARCH_HAS_GET_REGS
+int ptrace_get_regs(int pid, user_regs_struct_t *regs)
+{
+ struct iovec iov;
+
+ iov.iov_base = regs;
+ iov.iov_len = sizeof(user_regs_struct_t);
+ return ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
+}
+
+int ptrace_set_regs(int pid, user_regs_struct_t *regs)
+{
+ struct iovec iov;
+
+ iov.iov_base = regs;
+ iov.iov_len = sizeof(user_regs_struct_t);
+ return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov);
+}
+#endif /* ARCH_HAS_GET_REGS */
diff --git a/criu/Makefile.crtools b/criu/Makefile.crtools
index 3563de727063..60176cad56b2 100644
--- a/criu/Makefile.crtools
+++ b/criu/Makefile.crtools
@@ -59,7 +59,6 @@ obj-y += proc_parse.o
obj-y += protobuf-desc.o
obj-y += protobuf.o
obj-y += pstree.o
-obj-y += ptrace.o
obj-y += rbtree.o
obj-y += rst-malloc.o
obj-y += seccomp.o
diff --git a/criu/arch/aarch64/crtools.c b/criu/arch/aarch64/crtools.c
index c1da5ef01c2b..d6b1b1344722 100644
--- a/criu/arch/aarch64/crtools.c
+++ b/criu/arch/aarch64/crtools.c
@@ -30,10 +30,7 @@ void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *
bool arch_can_dump_task(pid_t pid)
{
- /*
- * TODO: Add proper check here
- */
- return true;
+ return ptrace_task_compatible(pid);
}
#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))(src)->e
diff --git a/criu/arch/aarch64/include/asm/restorer.h b/criu/arch/aarch64/include/asm/restorer.h
index e23ab11fbfbd..b842a74ef0ef 100644
--- a/criu/arch/aarch64/include/asm/restorer.h
+++ b/criu/arch/aarch64/include/asm/restorer.h
@@ -62,16 +62,6 @@ static inline void restore_tls(tls_t *ptls)
asm("msr tpidr_el0, %0" : : "r" (*ptls));
}
-static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
-{
- return 0;
-}
-
-static inline int ptrace_flush_breakpoints(pid_t pid)
-{
- return 0;
-}
-
static inline void *alloc_compat_syscall_stack(void) { return NULL; }
static inline void free_compat_syscall_stack(void *stack32) { }
static inline int
diff --git a/criu/arch/arm/crtools.c b/criu/arch/arm/crtools.c
index 5b6469d59407..84e759ad0a8d 100644
--- a/criu/arch/arm/crtools.c
+++ b/criu/arch/arm/crtools.c
@@ -34,10 +34,7 @@ void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *
bool arch_can_dump_task(pid_t pid)
{
- /*
- * TODO: Add proper check here
- */
- return true;
+ return ptrace_task_compatible(pid);
}
#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))((src)->ARM_##e)
diff --git a/criu/arch/arm/include/asm/restorer.h b/criu/arch/arm/include/asm/restorer.h
index 6a550b852240..1368d624e569 100644
--- a/criu/arch/arm/include/asm/restorer.h
+++ b/criu/arch/arm/include/asm/restorer.h
@@ -73,16 +73,6 @@ static inline void restore_tls(tls_t *ptls) {
);
}
-static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
-{
- return 0;
-}
-
-static inline int ptrace_flush_breakpoints(pid_t pid)
-{
- return 0;
-}
-
static inline void *alloc_compat_syscall_stack(void) { return NULL; }
static inline void free_compat_syscall_stack(void *stack32) { }
static inline int
diff --git a/criu/arch/ppc64/crtools.c b/criu/arch/ppc64/crtools.c
index 967caf2355cf..3d19b783240a 100644
--- a/criu/arch/ppc64/crtools.c
+++ b/criu/arch/ppc64/crtools.c
@@ -40,10 +40,7 @@ void parasite_setup_regs(unsigned long new_ip, void *stack, user_regs_struct_t *
bool arch_can_dump_task(pid_t pid)
{
- /*
- * TODO: We should detect 32bit task when BE support is done.
- */
- return true;
+ return ptrace_task_compatible(pid);
}
static UserPpc64FpstateEntry *copy_fp_regs(uint64_t *fpregs)
diff --git a/criu/arch/ppc64/include/asm/restorer.h b/criu/arch/ppc64/include/asm/restorer.h
index 58fc2b025396..51ef917b4848 100644
--- a/criu/arch/ppc64/include/asm/restorer.h
+++ b/criu/arch/ppc64/include/asm/restorer.h
@@ -54,16 +54,6 @@ int restore_nonsigframe_gpregs(UserPpc64RegsEntry *r);
/* Nothing to do, TLS is accessed through r13 */
static inline void restore_tls(tls_t *ptls) { (void)ptls; }
-static inline int ptrace_set_breakpoint(pid_t pid, void *addr)
-{
- return 0;
-}
-
-static inline int ptrace_flush_breakpoints(pid_t pid)
-{
- return 0;
-}
-
/*
* Defined in arch/ppc64/syscall-common-ppc64.S
*/
diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c
index 12a08bfb8f4b..952041d2428b 100644
--- a/criu/arch/x86/crtools.c
+++ b/criu/arch/x86/crtools.c
@@ -72,57 +72,14 @@ int kdat_compat_sigreturn_test(void)
}
#endif /* CONFIG_X86_64 */
-int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs);
-int arch_task_compatible(pid_t pid)
-{
- user_regs_struct_t r;
- int ret = ptrace_get_regs(pid, &r);
-
- if (ret)
- return -1;
-
- return !user_regs_native(&r);
-}
-
-#define USER32_CS 0x23
-#define USER_CS 0x33
-
-static bool ldt_task_selectors(pid_t pid)
-{
- unsigned long cs;
-
- errno = 0;
- /*
- * Offset of register must be from 64-bit set even for
- * compatible tasks. Fix this to support native i386 tasks
- */
- cs = ptrace(PTRACE_PEEKUSER, pid, offsetof(user_regs_struct64, cs), 0);
- if (errno != 0) {
- pr_perror("Can't get CS register for %d", pid);
- return -1;
- }
-
- return cs != USER_CS && cs != USER32_CS;
-}
-
bool arch_can_dump_task(pid_t pid)
{
- int ret = arch_task_compatible(pid);
-
- if (ret < 0)
- return false;
-
- if (ret && !kdat.has_compat_sigreturn) {
+ if (!kdat.has_compat_sigreturn) {
pr_err("Can't dump task %d running in 32-bit mode\n", pid);
return false;
}
- if (ldt_task_selectors(pid)) {
- pr_err("Can't dump task %d with LDT descriptors\n", pid);
- return false;
- }
-
- return true;
+ return ptrace_task_compatible(pid);
}
int save_task_regs(void *x, user_regs_struct_t *regs, user_fpregs_struct_t *fpregs)
@@ -216,49 +173,6 @@ int save_task_regs(void *x, user_regs_struct_t *regs, user_fpregs_struct_t *fpre
return 0;
}
-int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs)
-{
- struct iovec iov;
- int ret;
-
- iov.iov_base = ®s->native;
- iov.iov_len = sizeof(user_regs_struct64);
-
- ret = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
- if (ret == -1) {
- pr_perror("PTRACE_GETREGSET failed");
- return -1;
- }
-
- if (iov.iov_len == sizeof(regs->native)) {
- regs->__is_native = NATIVE_MAGIC;
- return ret;
- }
- if (iov.iov_len == sizeof(regs->compat)) {
- regs->__is_native = COMPAT_MAGIC;
- return ret;
- }
-
- pr_err("PTRACE_GETREGSET read %zu bytes for pid %d, but native/compat regs sizes are %zu/%zu bytes",
- iov.iov_len, pid,
- sizeof(regs->native), sizeof(regs->compat));
- return -1;
-}
-
-int ptrace_set_regs(pid_t pid, user_regs_struct_t *regs)
-{
- struct iovec iov;
-
- if (user_regs_native(regs)) {
- iov.iov_base = ®s->native;
- iov.iov_len = sizeof(user_regs_struct64);
- } else {
- iov.iov_base = ®s->compat;
- iov.iov_len = sizeof(user_regs_struct32);
- }
- return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov);
-}
-
static void alloc_tls(ThreadInfoX86 *ti, void **mempool)
{
int i;
@@ -563,62 +477,3 @@ int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
return 0;
}
#endif
-
-/* Copied from the gdb header gdb/nat/x86-dregs.h */
-
-/* Debug registers' indices. */
-#define DR_FIRSTADDR 0
-#define DR_LASTADDR 3
-#define DR_NADDR 4 /* The number of debug address registers. */
-#define DR_STATUS 6 /* Index of debug status register (DR6). */
-#define DR_CONTROL 7 /* Index of debug control register (DR7). */
-
-#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */
-#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */
-#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */
-
-/* Locally enable the break/watchpoint in the I'th debug register. */
-#define X86_DR_LOCAL_ENABLE(i) (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
-
-int ptrace_set_breakpoint(pid_t pid, void *addr)
-{
- int ret;
-
- /* Set a breakpoint */
- if (ptrace(PTRACE_POKEUSER, pid,
- offsetof(struct user, u_debugreg[DR_FIRSTADDR]),
- addr)) {
- pr_perror("Unable to setup a breakpoint into %d", pid);
- return -1;
- }
-
- /* Enable the breakpoint */
- if (ptrace(PTRACE_POKEUSER, pid,
- offsetof(struct user, u_debugreg[DR_CONTROL]),
- X86_DR_LOCAL_ENABLE(DR_FIRSTADDR))) {
- pr_perror("Unable to enable the breakpoint for %d", pid);
- return -1;
- }
-
- ret = ptrace(PTRACE_CONT, pid, NULL, NULL);
- if (ret) {
- pr_perror("Unable to restart the stopped tracee process %d", pid);
- return -1;
- }
-
- return 1;
-}
-
-int ptrace_flush_breakpoints(pid_t pid)
-{
- /* Disable the breakpoint */
- if (ptrace(PTRACE_POKEUSER, pid,
- offsetof(struct user, u_debugreg[DR_CONTROL]),
- 0)) {
- pr_perror("Unable to disable the breakpoint for %d", pid);
- return -1;
- }
-
- return 0;
-}
-
diff --git a/criu/arch/x86/include/asm/dump.h b/criu/arch/x86/include/asm/dump.h
index 61e53dced564..ca7cd675f21c 100644
--- a/criu/arch/x86/include/asm/dump.h
+++ b/criu/arch/x86/include/asm/dump.h
@@ -5,10 +5,6 @@ extern int save_task_regs(void *, user_regs_struct_t *, user_fpregs_struct_t *);
extern int arch_alloc_thread_info(CoreEntry *core);
extern void arch_free_thread_info(CoreEntry *core);
-#define ARCH_HAS_GET_REGS
-extern int ptrace_get_regs(pid_t pid, user_regs_struct_t *regs);
-extern int ptrace_set_regs(pid_t pid, user_regs_struct_t *regs);
-
static inline void core_put_tls(CoreEntry *core, tls_t tls)
{
ThreadInfoX86 *ti = core->thread_info;
diff --git a/criu/include/ptrace.h b/criu/include/ptrace.h
index b3343fdf83a5..7ad1de2323bd 100644
--- a/criu/include/ptrace.h
+++ b/criu/include/ptrace.h
@@ -1,80 +1,17 @@
#ifndef __CR_PTRACE_H__
#define __CR_PTRACE_H__
-#include <linux/types.h>
-#include <sys/ptrace.h>
+#include <stdint.h>
+#include <compel/ptrace.h>
#include "config.h"
-/* some constants for ptrace */
-#ifndef PTRACE_SEIZE
-# define PTRACE_SEIZE 0x4206
-#endif
-
-#ifndef PTRACE_O_SUSPEND_SECCOMP
-# define PTRACE_O_SUSPEND_SECCOMP (1 << 21)
-#endif
-
-#ifndef PTRACE_INTERRUPT
-# define PTRACE_INTERRUPT 0x4207
-#endif
-
-#ifndef PTRACE_LISTEN
-#define PTRACE_LISTEN 0x4208
-#endif
-
-#ifndef PTRACE_PEEKSIGINFO
-#define PTRACE_PEEKSIGINFO 0x4209
-
-/* Read signals from a shared (process wide) queue */
-#define PTRACE_PEEKSIGINFO_SHARED (1 << 0)
-#endif
-
#ifndef CONFIG_HAS_PTRACE_PEEKSIGINFO
struct ptrace_peeksiginfo_args {
- __u64 off; /* from which siginfo to start */
- __u32 flags;
- __u32 nr; /* how may siginfos to take */
+ uint64_t off; /* from which siginfo to start */
+ uint32_t flags;
+ uint32_t nr; /* how may siginfos to take */
};
#endif
-#ifndef PTRACE_GETREGSET
-# define PTRACE_GETREGSET 0x4204
-# define PTRACE_SETREGSET 0x4205
-#endif
-
-#ifndef PTRACE_GETSIGMASK
-# define PTRACE_GETSIGMASK 0x420a
-# define PTRACE_SETSIGMASK 0x420b
-#endif
-
-#ifndef PTRACE_SECCOMP_GET_FILTER
-#define PTRACE_SECCOMP_GET_FILTER 0x420c
-#endif
-
-#define PTRACE_SEIZE_DEVEL 0x80000000
-
-#define PTRACE_EVENT_FORK 1
-#define PTRACE_EVENT_VFORK 2
-#define PTRACE_EVENT_CLONE 3
-#define PTRACE_EVENT_EXEC 4
-#define PTRACE_EVENT_VFORK_DONE 5
-#define PTRACE_EVENT_EXIT 6
-#define PTRACE_EVENT_STOP 128
-
-#define PTRACE_O_TRACESYSGOOD 0x00000001
-#define PTRACE_O_TRACEFORK 0x00000002
-#define PTRACE_O_TRACEVFORK 0x00000004
-#define PTRACE_O_TRACECLONE 0x00000008
-#define PTRACE_O_TRACEEXEC 0x00000010
-#define PTRACE_O_TRACEVFORKDONE 0x00000020
-#define PTRACE_O_TRACEEXIT 0x00000040
-
-#define SI_EVENT(_si_code) (((_si_code) & 0xFFFF) >> 8)
-
-extern int suspend_seccomp(pid_t pid);
-extern int ptrace_peek_area(pid_t pid, void *dst, void *addr, long bytes);
-extern int ptrace_poke_area(pid_t pid, void *src, void *addr, long bytes);
-extern int ptrace_swap_area(pid_t pid, void *dst, void *src, long bytes);
-
#endif /* __CR_PTRACE_H__ */
diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c
index 09c9c895afc7..c5bf365f902c 100644
--- a/criu/parasite-syscall.c
+++ b/criu/parasite-syscall.c
@@ -68,26 +68,6 @@ unsigned long get_exec_start(struct vm_area_list *vmas)
return 0;
}
-#ifndef ARCH_HAS_GET_REGS
-static inline int ptrace_get_regs(int pid, user_regs_struct_t *regs)
-{
- struct iovec iov;
-
- iov.iov_base = regs;
- iov.iov_len = sizeof(user_regs_struct_t);
- return ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov);
-}
-
-static inline int ptrace_set_regs(int pid, user_regs_struct_t *regs)
-{
- struct iovec iov;
-
- iov.iov_base = regs;
- iov.iov_len = sizeof(user_regs_struct_t);
- return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov);
-}
-#endif
-
int parasite_send_fd(struct parasite_ctl *ctl, int fd)
{
int sk;
--
2.7.4
More information about the CRIU
mailing list