Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 76 additions & 3 deletions src/elfcore.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ extern "C" {
#define AT_SYSINFO_EHDR 33
#endif

#ifndef PTRACE_GETREGSET
#define PTRACE_GETREGSET 0x4204
#endif

#ifndef NT_PRSTATUS
#define NT_PRSTATUS 1
#endif

#ifndef NT_PRFPREG
#define NT_PRFPREG 2
#endif

#ifndef O_LARGEFILE
#if defined(__mips__)
#define O_LARGEFILE 0x2000
Expand Down Expand Up @@ -149,18 +161,32 @@ typedef struct fpregs {
uint32_t fir;
} fpregs;
#define regs mips_regs
#elif defined(__aarch64__)
typedef struct fpxregs { /* No extended FPU registers concept on aarch64 */
} fpxregs;
typedef struct fpregs { /* NEON/FP registers */
__uint128_t vregs[32]; /* 32x128-bit SIMD registers */
uint32_t fpsr; /* Floating-point status register */
uint32_t fpcr; /* Floating-point control register */
} fpregs;
#define regs aarch64_regs /* General purpose registers */
#endif

typedef struct elf_timeval { /* Time value with microsecond resolution */
long tv_sec; /* Seconds */
long tv_usec; /* Microseconds */
} elf_timeval;

/* On aarch64, signal.h -> sys/ucontext.h -> sys/procfs.h already defines
* struct elf_siginfo, so we must not redefine it.
*/
#if !defined(__aarch64__)
typedef struct elf_siginfo { /* Information about signal (unused) */
int32_t si_signo; /* Signal number */
int32_t si_code; /* Extra code */
int32_t si_errno; /* Errno */
} elf_siginfo;
#endif

typedef struct prstatus { /* Information about thread; includes CPU reg*/
elf_siginfo pr_info; /* Info associated with signal */
Expand All @@ -185,7 +211,7 @@ typedef struct prpsinfo { /* Information about process */
unsigned char pr_zomb; /* Zombie */
signed char pr_nice; /* Nice val */
unsigned long pr_flag; /* Flags */
#if defined(__x86_64__) || defined(__mips__)
#if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__)
uint32_t pr_uid; /* User ID */
uint32_t pr_gid; /* Group ID */
#else
Expand All @@ -201,7 +227,7 @@ typedef struct prpsinfo { /* Information about process */
} prpsinfo;

typedef struct core_user { /* Ptrace returns this data for thread state */
#ifndef __mips__
#if !defined(__mips__) && !defined(__aarch64__)
struct regs regs; /* CPU registers */
unsigned long fpvalid; /* True if math co-processor being used */
#if defined(__i386__) || defined(__x86_64__)
Expand Down Expand Up @@ -255,6 +281,8 @@ typedef struct core_user { /* Ptrace returns this data for thread state */
#define ELF_ARCH EM_ARM
#elif defined(__mips__)
#define ELF_ARCH EM_MIPS
#elif defined(__aarch64__)
#define ELF_ARCH EM_AARCH64
#endif

/* Wrap a class around system calls, in order to give us access to
Expand Down Expand Up @@ -1546,6 +1574,25 @@ static inline int GetParentRegs(void *frame, regs *cpu, fpregs *fp, fpxregs *fpx
pid_t pid = getppid();
if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0, (void *)0) == 0 && waitpid(pid, (void *)0, __WALL) >= 0) {
memset(scratch, 0xFF, sizeof(scratch));
#if defined(__aarch64__)
/* aarch64 uses PTRACE_GETREGSET with NT_PRSTATUS/NT_PRFPREG */
{
struct iovec iov;
iov.iov_base = scratch;
iov.iov_len = sizeof(struct regs);
if (sys_ptrace(PTRACE_GETREGSET, pid, (void *)(uintptr_t)NT_PRSTATUS, &iov) == 0) {
memcpy(cpu, scratch, sizeof(struct regs));
SET_FRAME(*(Frame *)frame, *cpu);
iov.iov_base = scratch;
iov.iov_len = sizeof(struct fpregs);
if (sys_ptrace(PTRACE_GETREGSET, pid, (void *)(uintptr_t)NT_PRFPREG, &iov) == 0) {
memcpy(fp, scratch, sizeof(struct fpregs));
*hasSSE = 0;
rc = 1;
}
}
}
#else
if (sys_ptrace(PTRACE_GETREGS, pid, scratch, scratch) == 0) {
memcpy(cpu, scratch, sizeof(struct regs));
SET_FRAME(*(Frame *)frame, *cpu);
Expand All @@ -1566,6 +1613,7 @@ static inline int GetParentRegs(void *frame, regs *cpu, fpregs *fp, fpxregs *fpx
rc = 1;
}
}
#endif
}
sys_ptrace_detach(pid);

Expand Down Expand Up @@ -1678,6 +1726,31 @@ int InternalGetCoreDump(void *frame, int num_threads, pid_t *pids,
SET_FRAME(*(Frame *)frame, thread_regs[i]);
}
hasSSE = 0;
#elif defined(__aarch64__)
/* aarch64 uses PTRACE_GETREGSET with NT_PRSTATUS/NT_PRFPREG */
{
struct iovec iov;
iov.iov_base = scratch;
iov.iov_len = sizeof(struct regs);
if (sys_ptrace(PTRACE_GETREGSET, pids[i], (void *)(uintptr_t)NT_PRSTATUS, &iov) == 0) {
memcpy(thread_regs + i, scratch, sizeof(struct regs));
if (main_pid == pids[i]) {
SET_FRAME(*(Frame *)frame, thread_regs[i]);
}
iov.iov_base = scratch;
iov.iov_len = sizeof(struct fpregs);
if (sys_ptrace(PTRACE_GETREGSET, pids[i], (void *)(uintptr_t)NT_PRFPREG, &iov) == 0) {
memcpy(thread_fpregs + i, scratch, sizeof(struct fpregs));
hasSSE = 0;
} else {
goto ptrace;
}
} else {
ptrace:
ResumeAllProcessThreads(threads, pids);
goto error;
}
}
#else
memset(scratch, 0xFF, sizeof(scratch));
if (sys_ptrace(PTRACE_GETREGS, pids[i], scratch, scratch) == 0) {
Expand Down Expand Up @@ -1712,7 +1785,7 @@ int InternalGetCoreDump(void *frame, int num_threads, pid_t *pids,

/* Get parent's CPU registers, and user data structure */
{
#ifndef __mips__
#if !defined(__mips__) && !defined(__aarch64__)
for (i = 0; i < sizeof(struct core_user); i += sizeof(int)) {
sys_ptrace(PTRACE_PEEKUSER, pids[0], (void *)i, ((char *)&user) + i);
}
Expand Down
58 changes: 57 additions & 1 deletion src/elfcore.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ extern "C" {
/* We currently only support x86-32, x86-64, ARM, and MIPS on Linux.
* Porting to other related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__)) && defined(__linux)
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__) || defined(__aarch64__)) && defined(__linux)

#include <stdarg.h>
#include <stdint.h>
Expand Down Expand Up @@ -105,6 +105,17 @@ typedef struct mips_regs {
unsigned long cp0_cause;
unsigned long unused;
} mips_regs;
#elif defined(__aarch64__)
typedef struct aarch64_regs { /* General purpose registers */
#define BP uregs[29] /* Frame pointer (x29) */
#define SP sp_el0 /* Stack pointer */
#define IP pc_reg /* Program counter */
#define LR uregs[30] /* Link register (x30) */
uint64_t uregs[31]; /* x0-x30 */
uint64_t sp_el0; /* Stack pointer */
uint64_t pc_reg; /* Program counter */
uint64_t pstate; /* CPSR / PSTATE */
} aarch64_regs;
#endif

#if defined(__i386__) && defined(__GNUC__)
Expand Down Expand Up @@ -332,6 +343,51 @@ typedef struct Frame {
(r).lo = (f).mips_regs.lo; \
(r).cp0_epc = (f).mips_regs.cp0_epc; \
} while (0)
#elif defined(__aarch64__) && defined(__GNUC__)
/* AArch64: capture all general-purpose registers.
*/
typedef struct Frame {
struct aarch64_regs arm64;
int errno_;
pid_t tid;
} Frame;
#define FRAME(f) \
Frame f; \
do { \
f.errno_ = errno; \
f.tid = sys_gettid(); \
__asm__ volatile( \
"stp x0, x1, [%0, #0]\n" \
"stp x2, x3, [%0, #16]\n" \
"stp x4, x5, [%0, #32]\n" \
"stp x6, x7, [%0, #48]\n" \
"stp x8, x9, [%0, #64]\n" \
"stp x10, x11, [%0, #80]\n" \
"stp x12, x13, [%0, #96]\n" \
"stp x14, x15, [%0, #112]\n" \
"stp x16, x17, [%0, #128]\n" \
"stp x18, x19, [%0, #144]\n" \
"stp x20, x21, [%0, #160]\n" \
"stp x22, x23, [%0, #176]\n" \
"stp x24, x25, [%0, #192]\n" \
"stp x26, x27, [%0, #208]\n" \
"stp x28, x29, [%0, #224]\n" \
"str x30, [%0, #240]\n" \
"mov x1, sp\n" \
"str x1, [%0, #248]\n" /* sp */ \
"adr x1, .\n" \
"str x1, [%0, #256]\n" /* pc */ \
"mrs x1, nzcv\n" \
"str x1, [%0, #264]\n" /* pstate */ \
: \
: "r"(&f.arm64) \
: "x1", "memory"); \
} while (0)
#define SET_FRAME(f, r) \
do { \
errno = (f).errno_; \
(r) = (f).arm64; \
} while (0)
#else
/* If we do not have a hand-optimized assembly version of the FRAME()
* macro, we cannot reliably unroll the stack. So, we show a few additional
Expand Down
Loading