aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShuai Xue <xueshuai@linux.alibaba.com>2022-10-18 22:46:13 +0800
committerTony Luck <tony.luck@intel.com>2022-10-18 10:03:26 -0700
commit0353291ec4a1993532baba9d72701a99c12dcd40 (patch)
treeff6d1afb73f0bbc31ace11ff4e017d407fd9e6fa
parentc8aef396cc8ff29f5c2dcc6dd6dc56ed2d9202ad (diff)
downloadras-tools-0353291ec4a1993532baba9d72701a99c12dcd40.tar.gz
hornet: extend ptrace with PTRACE_GETREGSET on arm64 platform
hornet use ptrace(2) with PTRACE_GETREGS request to read the tracee's general-purpose registers, but it does not work on arm64 platform. To extend hornet on both X86 and arm64 platform, retrieve rip or PC in an architecture-dependent way when PTRACE_GETREGS is not available. Signed-off-by: Shuai Xue <xueshuai@linux.alibaba.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r--hornet.c57
1 files changed, 47 insertions, 10 deletions
diff --git a/hornet.c b/hornet.c
index bfae9d6..9763e5e 100644
--- a/hornet.c
+++ b/hornet.c
@@ -27,6 +27,9 @@
#include <sys/wait.h>
#include <sys/user.h>
#include <sys/ptrace.h>
+#include <linux/ptrace.h>
+#include <sys/uio.h>
+#include <elf.h>
static char *progname;
@@ -52,14 +55,47 @@ static void usage(void)
#define EINJ_DOIT "/sys/kernel/debug/apei/einj/error_inject"
#define check_ptrace(req, pid, addr, data) \
- do { \
- if (ptrace(req, pid, addr, data) == -1) { \
+ do { \
+ if (ptrace(req, pid, addr, data) == -1) { \
fprintf(stderr, "Failed to run "#req": %s\n", \
- strerror(errno)); \
- return 1; \
- } \
+ strerror(errno)); \
+ return errno; \
+ } \
} while (0)
+#if defined(__x86_64__)
+# define ARCH_REGS struct user_regs_struct
+# define ARCH_PC(_regs) (_regs).rip
+#elif defined(__arm__)
+# define ARCH_REGS struct pt_regs
+# define ARCH_PC(_regs) (_regs).ARM_pc
+#elif defined(__aarch64__)
+# define ARCH_REGS struct user_pt_regs
+# define ARCH_PC(_regs) (_regs).pc
+# endif
+
+/*
+ * Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for
+ * architectures without HAVE_ARCH_TRACEHOOK (e.g. User-mode Linux).
+ */
+#if defined(__x86_64__) || defined(__i386__) || defined(__mips__)
+# define ARCH_GETREGS(tracee, _regs) check_ptrace(PTRACE_GETREGS, tracee, 0, &(_regs))
+# define ARCH_SETREGS(tracee, _regs) check_ptrace(PTRACE_SETREGS, tracee, 0, &(_regs))
+#else
+# define ARCH_GETREGS(tracee, _regs) ({ \
+ struct iovec __v; \
+ __v.iov_base = &(_regs); \
+ __v.iov_len = sizeof(_regs); \
+ check_ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &__v); \
+ })
+# define ARCH_SETREGS(tracee, _regs) ({ \
+ struct iovec __v; \
+ __v.iov_base = &(_regs); \
+ __v.iov_len = sizeof(_regs); \
+ check_ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &__v); \
+ })
+#endif
+
static void wfile(char *file, unsigned long val)
{
FILE *fp;
@@ -210,7 +246,7 @@ int main(int argc, char **argv)
int c;
int status;
long lo, hi, phys, virt;
- struct user_regs_struct regs;
+ ARCH_REGS regs;
int ret = 0;
progname = argv[0];
@@ -240,8 +276,8 @@ int main(int argc, char **argv)
if (trace) {
check_ptrace(PTRACE_ATTACH, pid, NULL, NULL);
waitpid(pid, NULL, 0);
- check_ptrace(PTRACE_GETREGS, pid, NULL, &regs);
- virt = regs.rip;
+ ARCH_GETREGS(pid, regs);
+ virt = ARCH_PC(regs);
check_ptrace(PTRACE_PEEKTEXT, pid, virt, NULL);
lo = hi = addr = virt;
} else {
@@ -262,8 +298,9 @@ int main(int argc, char **argv)
wfile(EINJ_ADDR, phys);
wfile(EINJ_DOIT, 1);
- if (vflag) printf("%s: injected UC error at virt=%lx phys=%lx to pid=%d%s\n",
- progname, virt, phys, pid, trace == 1 ? "(ptrace)" : "");
+ if (vflag)
+ printf("%s: injected UC error at virt=%lx phys=%lx to pid=%d%s\n",
+ progname, virt, phys, pid, trace == 1 ? "(ptrace)" : "");
if (trace) {
sleep(1);