[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 = &regs;
+	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, &regs, &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, &regs, &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, &regs, &fpregs);
+	if (ret)
+		return ret;
+
+	return save(arg, &regs, &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 = &regs->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 = &regs->native;
+		iov.iov_len = sizeof(user_regs_struct64);
+	} else {
+		iov.iov_base = &regs->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(&regs) ? "native" : "compat");
+
+	/* Did we come from a system call? */
+	if (get_signed_user_reg(&regs, orig_ax) >= 0) {
+		/* Restart the system call */
+		switch (get_signed_user_reg(&regs, ax)) {
+		case -ERESTARTNOHAND:
+		case -ERESTARTSYS:
+		case -ERESTARTNOINTR:
+			set_user_reg(&regs, ax, get_user_reg(&regs, orig_ax));
+			set_user_reg(&regs, ip, get_user_reg(&regs, ip) - 2);
+			break;
+		case -ERESTART_RESTARTBLOCK:
+			pr_warn("Will restore %d with interrupted system call\n", pid);
+			set_user_reg(&regs, 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, &regs, 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 = &regs->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 = &regs->native;
-		iov.iov_len = sizeof(user_regs_struct64);
-	} else {
-		iov.iov_base = &regs->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