/* arch_args.h COPYRIGHT FUJITSU LIMITED 2017-2018 */ #ifndef ARCH_ARGS_H #define ARCH_ARGS_H #include #ifndef NT_ARM_SYSTEM_CALL #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ #endif /* !NT_ARM_SYSTEM_CALL */ typedef struct { struct user_pt_regs regs; unsigned long orig_x0; unsigned long ret_value; pid_t target_pid; int bypass; } syscall_args; enum ptrace_syscall_dir { PTRACE_SYSCALL_ENTER = 0, PTRACE_SYSCALL_EXIT, }; static inline int syscall_enter(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return -1; } switch (args->regs.regs[7]) { case PTRACE_SYSCALL_ENTER: return 1; case PTRACE_SYSCALL_EXIT: return 0; default: printf("%s: x7 is neither SYSCALL_ENTER nor SYSCALL_EXIT.\n", __func__); return -1; } } static inline int get_syscall_args(int pid, syscall_args *args) { struct iovec iov; long ret = -1; if (!args) { printf("%s: input args is NULL.\n", __func__); return -1; } args->target_pid = pid; args->bypass = 0; memset(&iov, 0, sizeof(iov)); iov.iov_base = &args->regs; iov.iov_len = sizeof(args->regs); ret = ptrace(PTRACE_GETREGSET, pid, NT_PRSTATUS, &iov); if (!ret) { if (syscall_enter(args)) { args->orig_x0 = args->regs.regs[0]; args->ret_value = 0; } else { /* orig_x0 is saved */ args->ret_value = args->regs.regs[0]; } } return ret; } static inline int set_syscall_args(int pid, syscall_args *args) { struct iovec iov; if (!args) { printf("%s: input args is NULL.\n", __func__); return -1; } memset(&iov, 0, sizeof(iov)); iov.iov_base = &args->regs; iov.iov_len = sizeof(args->regs); return ptrace(PTRACE_SETREGSET, pid, NT_PRSTATUS, &iov); } static inline unsigned long get_syscall_number(syscall_args *args) { int sysno = -1; long ret = -1; struct iovec iov; if (!args) { printf("%s: input args is NULL.\n", __func__); return -1; } memset(&iov, 0, sizeof(iov)); iov.iov_base = &sysno; iov.iov_len = sizeof(sysno); ret = ptrace(PTRACE_GETREGSET, args->target_pid, NT_ARM_SYSTEM_CALL, &iov); if (ret) { printf("%s: ptrace(PTRACE_GETREGSET) failed. (%d)", __func__, errno); } return sysno; } static inline unsigned long get_syscall_arg1(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return 0; } return args->orig_x0; } static inline unsigned long get_syscall_arg2(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return 0; } return args->regs.regs[1]; } static inline unsigned long get_syscall_arg3(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return 0; } return args->regs.regs[2]; } static inline unsigned long get_syscall_arg4(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return 0; } return args->regs.regs[3]; } static inline unsigned long get_syscall_arg5(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return 0; } return args->regs.regs[4]; } static inline unsigned long get_syscall_arg6(syscall_args *args) { if (!args) { printf("%s: input args is NULL.\n", __func__); return 0; } return args->regs.regs[5]; } static inline void set_syscall_number(syscall_args *args, unsigned long value) { int sysno = (int)value; long ret = -1; struct iovec iov; if (!args) { printf("%s: input args is NULL.\n", __func__); return; } memset(&iov, 0, sizeof(iov)); iov.iov_base = &sysno; iov.iov_len = sizeof(sysno); ret = ptrace(PTRACE_SETREGSET, args->target_pid, NT_ARM_SYSTEM_CALL, &iov); if (ret) { printf("%s: ptrace(PTRACE_GETREGSET) failed. (%d)", __func__, errno); } else { if (value == (unsigned long)-1) { args->bypass = 1; } } } static inline void set_syscall_ret_or_arg1(syscall_args *args, unsigned long value, int ret_flag) { /* called by set_syscall_return() */ if (ret_flag == 1) { /* stopped syscall-enter */ if (syscall_enter(args) == 1) { /* syscall no bypass */ if (args->bypass != 1) { /* no effect */ goto out; } } } /* called by set_syscall_arg1() */ else if (ret_flag == 0) { /* stopped syscall-return */ if (syscall_enter(args) == 0) { /* no effect */ goto out; } /* set original arg1 */ args->orig_x0 = value; } /* illegal ret_flag */ else { /* no effect */ goto out; } /* set value */ args->regs.regs[0] = value; out: return; } static inline void set_syscall_return(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } set_syscall_ret_or_arg1(args, value, 1); } static inline void set_syscall_arg1(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } set_syscall_ret_or_arg1(args, value, 0); } static inline void set_syscall_arg2(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } args->regs.regs[1] = value; } static inline void set_syscall_arg3(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } args->regs.regs[2] = value; } static inline void set_syscall_arg4(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } args->regs.regs[3] = value; } static inline void set_syscall_arg5(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } args->regs.regs[4] = value; } static inline void set_syscall_arg6(syscall_args *args, unsigned long value) { if (!args) { printf("%s: input args is NULL.\n", __func__); return; } args->regs.regs[5] = value; } #endif /* !ARCH_ARGS_H */