summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Chen <tracelog@fb.com>2017-08-07 09:32:27 -0700
committerDave Watson <davejwatson@fb.com>2017-08-07 09:33:23 -0700
commit0314ff8522a6c991db26d5fb935e270d260e6dc0 (patch)
treeaa4a02912f7ee21c7d7a837705d04ed518996546
parentcc0c170f535deadb48e285326a42d3fafabf411d (diff)
downloadlibunwind-0314ff8522a6c991db26d5fb935e270d260e6dc0.tar.gz
aarch64: Use PTRACE_GETREGSET if available
In remote ptrace mode, we currently use PTRACE_PEEKUSER to read the registers. PTRACE_PEEKUSER only works on x86 or arm 32 bit compatibility mode on linux. On aarch64 system, it always return -EIO. https://github.com/torvalds/linux/blob/master/kernel/ptrace.c#L885-L1102 PTRACE_GETREGSET is the newer and more supported way of reading registers. Use that if it's available.
-rw-r--r--configure.ac2
-rw-r--r--src/ptrace/_UPT_access_reg.c45
2 files changed, 45 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index e11ba8bf..d4007535 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,7 +56,7 @@ AC_CHECK_TYPES([struct elf_prstatus, struct prstatus], [], [],
#endif
])
-AC_CHECK_DECLS([PTRACE_POKEUSER, PTRACE_POKEDATA,
+AC_CHECK_DECLS([PTRACE_POKEUSER, PTRACE_POKEDATA, PTRACE_SETREGSET,
PTRACE_TRACEME, PTRACE_CONT, PTRACE_SINGLESTEP,
PTRACE_SYSCALL, PT_IO, PT_GETREGS,
PT_GETFPREGS, PT_CONTINUE, PT_TRACE_ME,
diff --git a/src/ptrace/_UPT_access_reg.c b/src/ptrace/_UPT_access_reg.c
index ae71608b..ce25c783 100644
--- a/src/ptrace/_UPT_access_reg.c
+++ b/src/ptrace/_UPT_access_reg.c
@@ -34,7 +34,50 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
# include "tdep-ia64/rse.h"
#endif
-#if HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE
+#if HAVE_DECL_PTRACE_SETREGSET
+#include <sys/uio.h>
+int
+_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
+ int write, void *arg)
+{
+ struct UPT_info *ui = arg;
+ pid_t pid = ui->pid;
+ gregset_t regs;
+ char *r;
+ struct iovec loc;
+
+#if UNW_DEBUG
+ Debug(16, "using getregset: reg: %s [%u], val: %lx, write: %u\n",
+ unw_regname(reg), (unsigned) reg, (long) val, write);
+
+ if (write)
+ Debug (16, "%s [%u] <- %lx\n", unw_regname (reg), (unsigned) reg, (long) *val);
+#endif
+ if ((unsigned) reg >= ARRAY_SIZE (_UPT_reg_offset))
+ {
+ errno = EINVAL;
+ goto badreg;
+ }
+
+ loc.iov_base = &regs;
+ loc.iov_len = sizeof(regs);
+
+ r = (char *)&regs + _UPT_reg_offset[reg];
+ if (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, &loc) == -1)
+ goto badreg;
+ if (write) {
+ memcpy(r, val, sizeof(unw_word_t));
+ if (ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &loc) == -1)
+ goto badreg;
+ } else
+ memcpy(val, r, sizeof(unw_word_t));
+ return 0;
+
+badreg:
+ Debug (1, "bad register %s [%u] (error: %s)\n", unw_regname(reg), reg, strerror (errno));
+ return -UNW_EBADREG;
+}
+#elif HAVE_DECL_PTRACE_POKEUSER || HAVE_TTRACE
int
_UPT_access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val,
int write, void *arg)