diff --git a/src/elfcore.cc b/src/elfcore.cc index c6a436d..d3e0409 100644 --- a/src/elfcore.cc +++ b/src/elfcore.cc @@ -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 @@ -149,6 +161,15 @@ 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 */ @@ -156,11 +177,16 @@ typedef struct elf_timeval { /* Time value with microsecond resolution */ 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 */ @@ -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 @@ -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__) @@ -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 @@ -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); @@ -1566,6 +1613,7 @@ static inline int GetParentRegs(void *frame, regs *cpu, fpregs *fp, fpxregs *fpx rc = 1; } } +#endif } sys_ptrace_detach(pid); @@ -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) { @@ -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); } diff --git a/src/elfcore.h b/src/elfcore.h index e481740..f553572 100644 --- a/src/elfcore.h +++ b/src/elfcore.h @@ -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 #include @@ -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__) @@ -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 diff --git a/src/linux_syscall_support.h b/src/linux_syscall_support.h index e407150..19445dd 100644 --- a/src/linux_syscall_support.h +++ b/src/linux_syscall_support.h @@ -76,7 +76,7 @@ /* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux. * Porting to other related platforms should not be difficult. */ -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__) || defined(__PPC__)) && \ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__) || defined(__PPC__) || defined(__aarch64__)) && \ defined(__linux) #ifndef SYS_CPLUSPLUS @@ -380,7 +380,7 @@ struct kernel_stat { unsigned __unused4; unsigned __unused5; }; -#elif defined(__x86_64__) +#elif defined(__x86_64__) || defined(__aarch64__) struct kernel_stat { unsigned long st_dev; unsigned long st_ino; @@ -1030,6 +1030,91 @@ struct kernel_statfs { #define __NR_move_pages 301 #endif /* End of powerpc defininitions */ +#elif defined(__aarch64__) +/* aarch64 uses the "new-style" generic syscall table. + * Most numbers come from , but we provide fallbacks. + */ +#ifndef __NR_setresuid +#define __NR_setresuid 147 +#define __NR_setresgid 149 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 134 +#define __NR_rt_sigprocmask 135 +#define __NR_rt_sigpending 136 +#define __NR_rt_sigsuspend 133 +#endif +#ifndef __NR_rt_sigreturn +#define __NR_rt_sigreturn 139 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 67 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 68 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 61 +#endif +#ifndef __NR_gettid +#define __NR_gettid 178 +#endif +#ifndef __NR_readahead +#define __NR_readahead 213 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 5 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 6 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 8 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 9 +#endif +#ifndef __NR_futex +#define __NR_futex 98 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 96 +#endif +#ifndef __NR_openat +#define __NR_openat 56 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 79 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 35 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 239 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 223 +#endif +#ifndef __NR_readlinkat +#define __NR_readlinkat 78 +#endif +#ifndef __NR_dup3 +#define __NR_dup3 24 +#endif +#ifndef __NR_pipe2 +#define __NR_pipe2 59 +#endif +#ifndef __NR_getpgid +#define __NR_getpgid 155 +#endif +#ifndef __NR_ppoll +#define __NR_ppoll 73 +#endif +/* End of aarch64 definitions */ #endif /* After forking, we must make sure to only call system calls. */ @@ -1091,7 +1176,7 @@ struct kernel_statfs { #endif #undef LSS_RETURN -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__)) +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__aarch64__)) /* Failing system calls return a negative result in the range of * -1..-4095. These are "errno" values with the sign inverted. */ @@ -2008,57 +2093,290 @@ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, int flags, } LSS_RETURN(int, __ret, __err); } +#elif defined(__aarch64__) +#undef LSS_REG +#define LSS_REG(r, a) register long __x##r __asm__("x" #r) = (long)(a) +#undef LSS_BODY +#define LSS_BODY(type, name, args...) \ + register long __res_x0 __asm__("x0"); \ + long __res; \ + __asm__ __volatile__("mov x8, %1\n" \ + "svc 0x0\n" \ + : "=r"(__res_x0) \ + : "i"(__NR_##name), ##args \ + : "x8", "memory"); \ + __res = __res_x0; \ + LSS_RETURN(type, __res) +#undef _syscall0 +#define _syscall0(type, name) \ + type LSS_NAME(name)(void) { LSS_BODY(type, name); } +#undef _syscall1 +#define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); \ + LSS_BODY(type, name, "r"(__x0)); \ + } +#undef _syscall2 +#define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); \ + LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__x0), "r"(__x1)); \ + } +#undef _syscall3 +#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); \ + LSS_REG(1, arg2); \ + LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__x0), "r"(__x1), "r"(__x2)); \ + } +#undef _syscall4 +#define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); \ + LSS_REG(1, arg2); \ + LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__x0), "r"(__x1), "r"(__x2), "r"(__x3)); \ + } +#undef _syscall5 +#define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ + LSS_REG(0, arg1); \ + LSS_REG(1, arg2); \ + LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__x0), "r"(__x1), "r"(__x2), "r"(__x3), "r"(__x4)); \ + } +#undef _syscall6 +#define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); \ + LSS_REG(1, arg2); \ + LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_REG(4, arg5); \ + LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__x0), "r"(__x1), "r"(__x2), "r"(__x3), "r"(__x4), "r"(__x5)); \ + } +LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register long __res_x0 __asm__("x0"); + register int (*__fn)(void *) __asm__("x0") = fn; + register void *__cstack __asm__("x1") = child_stack; + register int __flags __asm__("x2") = flags; + register void *__arg __asm__("x3") = arg; + register int *__ptid __asm__("x4") = parent_tidptr; + register void *__tls __asm__("x5") = newtls; + register int *__ctid __asm__("x6") = child_tidptr; + __asm__ __volatile__( + /* Sanity check: fn and child_stack must not be NULL */ + "cbz x0, 1f\n" + "cbz x1, 1f\n" + + /* Save fn and arg on the child stack */ + "stp x0, x3, [x1, #-16]!\n" + + /* Shuffle args for clone syscall: + * x0 = flags, x1 = child_stack (already), + * x2 = parent_tidptr, x3 = newtls, x4 = child_tidptr + */ + "mov x0, x2\n" /* flags */ + /* x1 is already child_stack */ + "mov x2, x4\n" /* parent_tidptr */ + "mov x3, x5\n" /* newtls */ + "mov x4, x6\n" /* child_tidptr */ + "mov x8, %1\n" /* __NR_clone */ + "svc 0x0\n" + + /* In parent? (x0 != 0) */ + "cbnz x0, 1f\n" + + /* In child: call fn(arg) */ + "ldp x1, x0, [sp], #16\n" /* x1 = fn, x0 = arg */ + "blr x1\n" + + /* Call _exit(retval) */ + "mov x8, %2\n" + "svc 0x0\n" + + "1:\n" + : "=r"(__res_x0) + : "i"(__NR_clone), "i"(__NR_exit), + "r"(__fn), "r"(__cstack), "r"(__flags), "r"(__arg), + "r"(__ptid), "r"(__tls), "r"(__ctid) + : "x8", "memory"); + __res = __res_x0; + } + LSS_RETURN(int, __res); +} +LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On aarch64, the kernel requires a restorer function that calls + * rt_sigreturn. We provide our own here. This function returns a + * pointer to the restorer trampoline without executing it. + */ + void (*res)(void); + __asm__ __volatile__( + "adr %0, 1f\n" + "b 2f\n" + ".align 4\n" + "1:mov x8, %1\n" + " svc 0x0\n" + "2:\n" + : "=r"(res) + : "i"(__NR_rt_sigreturn)); + return res; +} #endif #define __NR__exit __NR_exit #define __NR__gettid __NR_gettid #define __NR__mremap __NR_mremap -LSS_INLINE _syscall1(int, chdir, const char *, p) LSS_INLINE _syscall1(int, close, int, f) LSS_INLINE - _syscall1(int, dup, int, f) LSS_INLINE _syscall2(int, dup2, int, s, int, d) LSS_INLINE - _syscall3(int, execve, const char *, f, const char *const *, a, const char *const *, e) LSS_INLINE - _syscall1(int, _exit, int, e) LSS_INLINE _syscall3(int, fcntl, int, f, int, c, long, a) LSS_INLINE - _syscall0(pid_t, fork) LSS_INLINE _syscall2(int, fstat, int, f, struct kernel_stat *, b) LSS_INLINE - _syscall2(int, fstatfs, int, f, struct kernel_statfs *, b) LSS_INLINE - _syscall4(int, futex, int *, a, int, o, int, v, struct kernel_timespec *, t) LSS_INLINE - _syscall3(int, getdents, int, f, struct kernel_dirent *, d, int, c) LSS_INLINE - _syscall3(int, getdents64, int, f, struct kernel_dirent64 *, d, int, c) LSS_INLINE - _syscall0(gid_t, getegid) LSS_INLINE _syscall0(uid_t, geteuid) LSS_INLINE _syscall0(pid_t, getpgrp) LSS_INLINE - _syscall0(pid_t, getpid) LSS_INLINE _syscall0(pid_t, getppid) LSS_INLINE - _syscall2(int, getpriority, int, a, int, b) LSS_INLINE - _syscall2(int, getrlimit, int, r, struct kernel_rlimit *, l) LSS_INLINE - _syscall1(pid_t, getsid, pid_t, p) LSS_INLINE _syscall0(pid_t, _gettid) LSS_INLINE - _syscall5(int, setxattr, const char *, p, const char *, n, const void *, v, size_t, s, int, f) LSS_INLINE - _syscall5(int, lsetxattr, const char *, p, const char *, n, const void *, v, size_t, s, int, f) LSS_INLINE - _syscall4(ssize_t, getxattr, const char *, p, const char *, n, void *, v, size_t, s) LSS_INLINE - _syscall4(ssize_t, lgetxattr, const char *, p, const char *, n, void *, v, size_t, s) LSS_INLINE - _syscall2(int, kill, pid_t, p, int, s) LSS_INLINE _syscall3(off_t, lseek, int, f, off_t, o, int, w) LSS_INLINE - _syscall2(int, munmap, void *, s, size_t, l) LSS_INLINE - _syscall6(long, move_pages, pid_t, p, unsigned long, n, void **, g, int *, d, int *, s, int, f) LSS_INLINE - _syscall5(void *, _mremap, void *, o, size_t, os, size_t, ns, unsigned long, f, void *, a) LSS_INLINE - _syscall3(int, open, const char *, p, int, f, int, m) LSS_INLINE - _syscall3(int, poll, struct kernel_pollfd *, u, unsigned int, n, int, t) LSS_INLINE - _syscall2(int, prctl, int, o, long, a) LSS_INLINE - _syscall4(long, ptrace, int, r, pid_t, p, void *, a, void *, d) LSS_INLINE - _syscall3(ssize_t, read, int, f, void *, b, size_t, c) LSS_INLINE - _syscall3(int, readlink, const char *, p, char *, b, size_t, s) LSS_INLINE - _syscall4(int, rt_sigaction, int, s, const struct kernel_sigaction *, a, struct kernel_sigaction *, o, size_t, - c) LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, size_t, c) LSS_INLINE - _syscall4(int, rt_sigprocmask, int, h, const struct kernel_sigset_t *, s, struct kernel_sigset_t *, o, size_t, c); +LSS_INLINE _syscall1(int, chdir, const char *, p) +LSS_INLINE _syscall1(int, close, int, f) +LSS_INLINE _syscall1(int, dup, int, f) +#if !defined(__aarch64__) +LSS_INLINE _syscall2(int, dup2, int, s, int, d) +#endif +LSS_INLINE _syscall3(int, execve, const char *, f, const char *const *, a, const char *const *, e) +LSS_INLINE _syscall1(int, _exit, int, e) +LSS_INLINE _syscall3(int, fcntl, int, f, int, c, long, a) +#if !defined(__aarch64__) +LSS_INLINE _syscall0(pid_t, fork) +#endif +LSS_INLINE _syscall2(int, fstat, int, f, struct kernel_stat *, b) +LSS_INLINE _syscall2(int, fstatfs, int, f, struct kernel_statfs *, b) +LSS_INLINE _syscall4(int, futex, int *, a, int, o, int, v, struct kernel_timespec *, t) +#if !defined(__aarch64__) +LSS_INLINE _syscall3(int, getdents, int, f, struct kernel_dirent *, d, int, c) +#endif +LSS_INLINE _syscall3(int, getdents64, int, f, struct kernel_dirent64 *, d, int, c) +LSS_INLINE _syscall0(gid_t, getegid) +LSS_INLINE _syscall0(uid_t, geteuid) +#if !defined(__aarch64__) +LSS_INLINE _syscall0(pid_t, getpgrp) +#endif +LSS_INLINE _syscall0(pid_t, getpid) +LSS_INLINE _syscall0(pid_t, getppid) +LSS_INLINE _syscall2(int, getpriority, int, a, int, b) +LSS_INLINE _syscall2(int, getrlimit, int, r, struct kernel_rlimit *, l) +LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) +LSS_INLINE _syscall0(pid_t, _gettid) +LSS_INLINE _syscall5(int, setxattr, const char *, p, const char *, n, const void *, v, size_t, s, int, f) +LSS_INLINE _syscall5(int, lsetxattr, const char *, p, const char *, n, const void *, v, size_t, s, int, f) +LSS_INLINE _syscall4(ssize_t, getxattr, const char *, p, const char *, n, void *, v, size_t, s) +LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *, p, const char *, n, void *, v, size_t, s) +LSS_INLINE _syscall2(int, kill, pid_t, p, int, s) +LSS_INLINE _syscall3(off_t, lseek, int, f, off_t, o, int, w) +LSS_INLINE _syscall2(int, munmap, void *, s, size_t, l) +LSS_INLINE _syscall6(long, move_pages, pid_t, p, unsigned long, n, void **, g, int *, d, int *, s, int, f) +LSS_INLINE _syscall5(void *, _mremap, void *, o, size_t, os, size_t, ns, unsigned long, f, void *, a) +#if !defined(__aarch64__) +LSS_INLINE _syscall3(int, open, const char *, p, int, f, int, m) +#endif +#if !defined(__aarch64__) +LSS_INLINE _syscall3(int, poll, struct kernel_pollfd *, u, unsigned int, n, int, t) +#endif +LSS_INLINE _syscall2(int, prctl, int, o, long, a) +LSS_INLINE _syscall4(long, ptrace, int, r, pid_t, p, void *, a, void *, d) +LSS_INLINE _syscall3(ssize_t, read, int, f, void *, b, size_t, c) +#if !defined(__aarch64__) +LSS_INLINE _syscall3(int, readlink, const char *, p, char *, b, size_t, s) +#endif +LSS_INLINE _syscall4(int, rt_sigaction, int, s, const struct kernel_sigaction *, a, struct kernel_sigaction *, o, size_t, c) +LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, size_t, c) +LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, const struct kernel_sigset_t *, s, struct kernel_sigset_t *, o, size_t, c); LSS_INLINE _syscall2(int, rt_sigsuspend, const struct kernel_sigset_t *, s, size_t, c); -LSS_INLINE _syscall3(int, sched_getaffinity, pid_t, p, unsigned int, l, unsigned long *, m) LSS_INLINE - _syscall3(int, sched_setaffinity, pid_t, p, unsigned int, l, unsigned long *, m) LSS_INLINE - _syscall0(int, sched_yield) LSS_INLINE _syscall1(long, set_tid_address, int *, t) LSS_INLINE - _syscall1(int, setfsgid, gid_t, g) LSS_INLINE _syscall1(int, setfsuid, uid_t, u) LSS_INLINE - _syscall2(int, setpgid, pid_t, p, pid_t, g) LSS_INLINE - _syscall3(int, setpriority, int, a, int, b, int, p) LSS_INLINE - _syscall3(int, setresgid, gid_t, r, gid_t, e, gid_t, s) LSS_INLINE - _syscall3(int, setresuid, uid_t, r, uid_t, e, uid_t, s) LSS_INLINE - _syscall2(int, setrlimit, int, r, const struct kernel_rlimit *, l) LSS_INLINE _syscall0(pid_t, setsid) LSS_INLINE - _syscall2(int, sigaltstack, const stack_t *, s, const stack_t *, o) LSS_INLINE - _syscall2(int, stat, const char *, f, struct kernel_stat *, b) LSS_INLINE - _syscall2(int, statfs, const char *, f, struct kernel_statfs *, b) LSS_INLINE - _syscall3(ssize_t, write, int, f, const void *, b, size_t, c) LSS_INLINE - _syscall3(ssize_t, writev, int, f, const struct kernel_iovec *, v, size_t, c) -#if defined(__x86_64__) || (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) +LSS_INLINE _syscall3(int, sched_getaffinity, pid_t, p, unsigned int, l, unsigned long *, m) +LSS_INLINE _syscall3(int, sched_setaffinity, pid_t, p, unsigned int, l, unsigned long *, m) +LSS_INLINE _syscall0(int, sched_yield) +LSS_INLINE _syscall1(long, set_tid_address, int *, t) +LSS_INLINE _syscall1(int, setfsgid, gid_t, g) +LSS_INLINE _syscall1(int, setfsuid, uid_t, u) +LSS_INLINE _syscall2(int, setpgid, pid_t, p, pid_t, g) +LSS_INLINE _syscall3(int, setpriority, int, a, int, b, int, p) +LSS_INLINE _syscall3(int, setresgid, gid_t, r, gid_t, e, gid_t, s) +LSS_INLINE _syscall3(int, setresuid, uid_t, r, uid_t, e, uid_t, s) +LSS_INLINE _syscall2(int, setrlimit, int, r, const struct kernel_rlimit *, l) +LSS_INLINE _syscall0(pid_t, setsid) +LSS_INLINE _syscall2(int, sigaltstack, const stack_t *, s, const stack_t *, o) +#if !defined(__aarch64__) +LSS_INLINE _syscall2(int, stat, const char *, f, struct kernel_stat *, b) +#endif +LSS_INLINE _syscall2(int, statfs, const char *, f, struct kernel_statfs *, b) +LSS_INLINE _syscall3(ssize_t, write, int, f, const void *, b, size_t, c) +LSS_INLINE _syscall3(ssize_t, writev, int, f, const struct kernel_iovec *, v, size_t, c) +LSS_INLINE _syscall1(int, getpgid, pid_t, p) +LSS_INLINE _syscall3(int, dup3, int, s, int, d, int, f) +LSS_INLINE _syscall2(int, pipe2, int *, p, int, f) +#if defined(__aarch64__) +/* aarch64 does not have legacy syscalls; provide wrappers using *at() variants */ +LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) +LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) +LSS_INLINE _syscall4(int, newfstatat, int, d, const char *, p, struct kernel_stat *, b, int, f) +LSS_INLINE _syscall4(int, readlinkat, int, d, const char *, p, char *, b, size_t, s) + +LSS_INLINE int LSS_NAME(open)(const char *pathname, int flags, int mode) { + return LSS_NAME(openat)(-100 /* AT_FDCWD */, pathname, flags, mode); +} +LSS_INLINE int LSS_NAME(stat)(const char *pathname, struct kernel_stat *buf) { + return LSS_NAME(newfstatat)(-100 /* AT_FDCWD */, pathname, buf, 0); +} +LSS_INLINE int LSS_NAME(dup2)(int oldfd, int newfd) { + return LSS_NAME(dup3)(oldfd, newfd, 0); +} +LSS_INLINE int LSS_NAME(pipe)(int *pipefd) { + return LSS_NAME(pipe2)(pipefd, 0); +} +LSS_INLINE pid_t LSS_NAME(fork)(void) { + /* aarch64 has no fork syscall; use clone with SIGCHLD directly */ + register long __res_x0 __asm__("x0"); + long __res; + __asm__ __volatile__( + "mov x0, %1\n" /* flags = SIGCHLD */ + "mov x1, #0\n" /* child_stack = NULL (use parent's) */ + "mov x2, #0\n" /* parent_tidptr = NULL */ + "mov x3, #0\n" /* newtls = NULL */ + "mov x4, #0\n" /* child_tidptr = NULL */ + "mov x8, %2\n" /* __NR_clone */ + "svc 0x0\n" + : "=r"(__res_x0) + : "i"(17 /* SIGCHLD */), "i"(__NR_clone) + : "x1", "x2", "x3", "x4", "x8", "memory"); + __res = __res_x0; + LSS_RETURN(pid_t, __res); +} +LSS_INLINE int LSS_NAME(readlink)(const char *pathname, char *buf, size_t bufsiz) { + return LSS_NAME(readlinkat)(-100 /* AT_FDCWD */, pathname, buf, bufsiz); +} +LSS_INLINE pid_t LSS_NAME(getpgrp)(void) { + return LSS_NAME(getpgid)(0); +} +LSS_INLINE int LSS_NAME(getdents)(int fd, struct kernel_dirent *dirp, int count) { + /* aarch64 has no getdents syscall; redirect to getdents64. + * Note: callers that use kernel_dirent must be updated to use + * kernel_dirent64 on aarch64 (see linuxthreads.cc). + */ + return LSS_NAME(getdents64)(fd, (struct kernel_dirent64 *)dirp, count); +} +LSS_INLINE _syscall5(int, ppoll, struct kernel_pollfd *, u, + unsigned int, n, const struct kernel_timespec *, t, + const struct kernel_sigset_t *, s, size_t, c) +LSS_INLINE int LSS_NAME(poll)(struct kernel_pollfd *fds, unsigned int nfds, int timeout) { + /* aarch64 has no poll syscall; use ppoll with a converted timeout */ + if (timeout >= 0) { + struct kernel_timespec ts; + ts.tv_sec = timeout / 1000; + ts.tv_nsec = (timeout % 1000) * 1000000L; + return LSS_NAME(ppoll)(fds, nfds, &ts, 0, 0); + } + return LSS_NAME(ppoll)(fds, nfds, 0, 0, 0); +} +#endif +#if defined(__x86_64__) || defined(__aarch64__) || (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) LSS_INLINE _syscall3(int, recvmsg, int, s, struct kernel_msghdr *, m, int, f) LSS_INLINE _syscall3(int, sendmsg, int, s, const struct kernel_msghdr *, m, int, f) LSS_INLINE _syscall6(int, sendto, int, s, const void *, m, size_t, l, int, f, const struct kernel_sockaddr *, a, int, @@ -2069,6 +2387,11 @@ LSS_INLINE _syscall3(int, sched_getaffinity, pid_t, p, unsigned int, l, unsigned #if defined(__x86_64__) LSS_INLINE _syscall6(void *, mmap, void *, s, size_t, l, int, p, int, f, int, d, __off64_t, o) LSS_INLINE _syscall4(int, newfstatat, int, d, const char *, p, struct kernel_stat *, b, int, f) +#endif +#if defined(__aarch64__) +LSS_INLINE _syscall6(void *, mmap, void *, s, size_t, l, int, p, int, f, int, d, __off64_t, o) +#endif +#if defined(__x86_64__) || defined(__aarch64__) LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { return LSS_NAME(setfsgid)(gid); @@ -2085,7 +2408,7 @@ LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { } LSS_INLINE int LSS_NAME(sigaction)(int signum, const struct kernel_sigaction *act, struct kernel_sigaction *oldact) { - /* On x86_64, the kernel requires us to always set our own + /* On x86_64 and aarch64, the kernel requires us to always set our own * SA_RESTORER in order to be able to return from a signal handler. * This function must have a "magic" signature that the "gdb" * (and maybe the kernel?) can recognize. @@ -2112,7 +2435,7 @@ LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG + 7) / 8); } #endif -#if defined(__x86_64__) || defined(__ARM_ARCH_3__) || (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) +#if defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_ARCH_3__) || (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, int *, s, int, o, struct kernel_rusage *, r) LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options) { @@ -2477,7 +2800,7 @@ LSS_INLINE _syscall4(int, fstatat64, int, d, const char *, p, struct kernel_stat return 0; } } -#else +#elif !defined(__aarch64__) LSS_INLINE _syscall1(int, pipe, int *, p) #endif /* TODO(csilvers): see if ppc can/should support this as well */ diff --git a/src/linuxthreads.cc b/src/linuxthreads.cc index 7eba5fa..1fd40c8 100644 --- a/src/linuxthreads.cc +++ b/src/linuxthreads.cc @@ -107,7 +107,14 @@ static int local_clone(int (*fn)(void *), void *arg, ...) { * is being debugged. This is OK and the error code will be reported * correctly. */ - return sys_clone(fn, (char *)&arg - 4096, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, arg, 0, 0, 0); + /* Ensure the child stack pointer is 16-byte aligned, as required by + * the AArch64 ABI. Without this, the child will get SIGBUS due to a + * Stack Alignment Fault (SCTLR_EL1.SA is set on Linux by default). + * On other architectures the mask is a no-op since stacks are already + * sufficiently aligned. + */ + return sys_clone(fn, (char *)(((unsigned long)&arg - 4096) & ~15UL), + CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_UNTRACED, arg, 0, 0, 0); } /* Local substitute for the atoi() function, which is not necessarily safe @@ -187,7 +194,8 @@ static volatile int *sig_pids, sig_num_threads, sig_proc, sig_marker; static void SignalHandler(int signum, siginfo_t *si, void *data) { if (sig_pids != NULL) { if (signum == SIGABRT) { - while (sig_num_threads-- > 0) { + while (sig_num_threads > 0) { + sig_num_threads = sig_num_threads - 1; /* Not sure if sched_yield is really necessary here, but it does not */ /* hurt, and it might be necessary for the same reasons that we have */ /* to do so in sys_ptrace_detach(). */ @@ -306,7 +314,9 @@ static void ListerThread(struct ListerParams *args) { * check there first, and then fall back on the older naming * convention if necessary. */ - if ((sig_proc = proc = c_open(*proc_path, O_RDONLY | O_DIRECTORY, 0)) < 0) { + proc = c_open(*proc_path, O_RDONLY | O_DIRECTORY, 0); + sig_proc = proc; + if (proc < 0) { if (*++proc_path != NULL) continue; goto failure; } @@ -331,9 +341,16 @@ static void ListerThread(struct ListerParams *args) { sig_num_threads = num_threads; sig_pids = pids; for (;;) { +#if defined(__aarch64__) + /* aarch64 has no getdents syscall; use getdents64 */ + struct kernel_dirent64 *entry; + char buf[4096]; + ssize_t nbytes = sys_getdents64(proc, (struct kernel_dirent64 *)buf, sizeof(buf)); +#else struct kernel_dirent *entry; char buf[4096]; ssize_t nbytes = sys_getdents(proc, (struct kernel_dirent *)buf, sizeof(buf)); +#endif if (nbytes < 0) goto failure; else if (nbytes == 0) { @@ -349,8 +366,13 @@ static void ListerThread(struct ListerParams *args) { } break; } +#if defined(__aarch64__) + for (entry = (struct kernel_dirent64 *)buf; entry < (struct kernel_dirent64 *)&buf[nbytes]; + entry = (struct kernel_dirent64 *)((char *)entry + entry->d_reclen)) { +#else for (entry = (struct kernel_dirent *)buf; entry < (struct kernel_dirent *)&buf[nbytes]; entry = (struct kernel_dirent *)((char *)entry + entry->d_reclen)) { +#endif if (entry->d_ino != 0) { const char *ptr = entry->d_name; pid_t pid; diff --git a/src/linuxthreads.h b/src/linuxthreads.h index 5eba4ac..1f89661 100644 --- a/src/linuxthreads.h +++ b/src/linuxthreads.h @@ -40,7 +40,7 @@ /* We currently only support x86-32 and x86-64 on Linux. Porting to other * related platforms should not be difficult. */ -#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__) || defined(__PPC__)) && \ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || defined(__mips__) || defined(__PPC__) || defined(__aarch64__)) && \ defined(__linux) /* Define the THREADS symbol to make sure that there is exactly one core dumper