diff options
author | Dave Brolley <brolley@redhat.com> | 2009-04-21 11:11:21 -0400 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2009-04-21 11:11:21 -0400 |
commit | 623a41aeb47995f6b5790e38f9e0e10959f98b4e (patch) | |
tree | e79bd792bfc682f842f804af1c119a751ae18c0c /runtime | |
parent | 09fd19d66b9e3318e9e33f604eb2dbe623955123 (diff) | |
parent | d0ea46ceac2e72fe0b86269ea50c004711148158 (diff) | |
download | systemtap-steved-623a41aeb47995f6b5790e38f9e0e10959f98b4e.tar.gz systemtap-steved-623a41aeb47995f6b5790e38f9e0e10959f98b4e.tar.xz systemtap-steved-623a41aeb47995f6b5790e38f9e0e10959f98b4e.zip |
Merge branch 'master' of git://sources.redhat.com/git/systemtap
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/syscall.h | 143 | ||||
-rw-r--r-- | runtime/task_finder.c | 3 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_i386.c | 13 | ||||
-rw-r--r-- | runtime/uprobes/uprobes_x86.c | 13 | ||||
-rw-r--r-- | runtime/uprobes2/uprobes_x86.h | 13 |
5 files changed, 152 insertions, 33 deletions
diff --git a/runtime/syscall.h b/runtime/syscall.h index 5e538389..ffc21efc 100644 --- a/runtime/syscall.h +++ b/runtime/syscall.h @@ -124,6 +124,14 @@ syscall_get_nr(struct task_struct *task, struct pt_regs *regs) static inline long syscall_get_nr(struct task_struct *task, struct pt_regs *regs) { + if ((long)regs->cr_ifs < 0) /* Not a syscall */ + return -1; + +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(regs)) + return regs->r1; +#endif + return regs->r15; } #endif @@ -320,38 +328,119 @@ syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, #endif #if defined(__ia64__) -#define syscall_get_arguments(task, regs, i, n, args) \ - __ia64_syscall_get_arguments(task, regs, i, n, args, &c->unwaddr) -static inline void -__ia64_syscall_get_arguments(struct task_struct *task, struct pt_regs *regs, - unsigned int i, unsigned int n, - unsigned long *args, unsigned long **cache) +/* Return TRUE if PT was created due to kernel-entry via a system-call. */ + +static inline int +in_syscall (struct pt_regs *pt) { - if (i + n > 6) { - _stp_error("invalid syscall arg request"); + return (long) pt->cr_ifs >= 0; +} + +struct syscall_get_set_args { + unsigned int i; + unsigned int n; + unsigned long *args; + struct pt_regs *regs; + int rw; +}; + +static void syscall_get_set_args_cb(struct unw_frame_info *info, void *data) +{ + struct syscall_get_set_args *args = data; + struct pt_regs *pt = args->regs; + unsigned long *krbs, cfm, ndirty; + int i, count; + + if (unw_unwind_to_user(info) < 0) return; + + cfm = pt->cr_ifs; + krbs = (unsigned long *)info->task + IA64_RBS_OFFSET/8; + ndirty = ia64_rse_num_regs(krbs, krbs + (pt->loadrs >> 19)); + + count = 0; + if (in_syscall(pt)) + count = min_t(int, args->n, cfm & 0x7f); + + for (i = 0; i < count; i++) { + if (args->rw) + *ia64_rse_skip_regs(krbs, ndirty + i + args->i) = + args->args[i]; + else + args->args[i] = *ia64_rse_skip_regs(krbs, + ndirty + i + args->i); } - switch (i) { - case 0: - if (!n--) break; - *args++ = ia64_fetch_register(32, regs, cache); - case 1: - if (!n--) break; - *args++ = ia64_fetch_register(33, regs, cache); - case 2: - if (!n--) break; - *args++ = ia64_fetch_register(34, regs, cache); - case 3: - if (!n--) break; - *args++ = ia64_fetch_register(35, regs, cache); - case 4: - if (!n--) break; - *args++ = ia64_fetch_register(36, regs, cache); - case 5: - if (!n--) break; - *args++ = ia64_fetch_register(37, regs, cache); + + if (!args->rw) { + while (i < args->n) { + args->args[i] = 0; + i++; + } + } +} + +void ia64_syscall_get_set_arguments(struct task_struct *task, + struct pt_regs *regs, unsigned int i, unsigned int n, + unsigned long *args, int rw) +{ + struct syscall_get_set_args data = { + .i = i, + .n = n, + .args = args, + .regs = regs, + .rw = rw, + }; + + if (task == current) + unw_init_running(syscall_get_set_args_cb, &data); + else { + struct unw_frame_info ufi; + memset(&ufi, 0, sizeof(ufi)); + unw_init_from_blocked_task(&ufi, task); + syscall_get_set_args_cb(&ufi, &data); + } +} + +static inline void syscall_get_arguments(struct task_struct *task, + struct pt_regs *regs, + unsigned int i, unsigned int n, + unsigned long *args) +{ + BUG_ON(i + n > 6); + +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(regs)) { + switch (i + n) { + case 6: + if (!n--) break; + *args++ = regs->r13; + case 5: + if (!n--) break; + *args++ = regs->r15; + case 4: + if (!n--) break; + *args++ = regs->r14; + case 3: + if (!n--) break; + *args++ = regs->r10; + case 2: + if (!n--) break; + *args++ = regs->r9; + case 1: + if (!n--) break; + *args++ = regs->r11; + case 0: + if (!n--) break; + default: + BUG(); + break; + } + + return; } +#endif + ia64_syscall_get_set_arguments(task, regs, i, n, args, 0); } #endif diff --git a/runtime/task_finder.c b/runtime/task_finder.c index fa6c296e..93b89cb9 100644 --- a/runtime/task_finder.c +++ b/runtime/task_finder.c @@ -1071,9 +1071,6 @@ __stp_utrace_task_finder_target_syscall_exit(enum utrace_resume_action action, int rc; struct mm_struct *mm; struct vm_area_struct *vma; -#if defined(__ia64__) - struct { unsigned long *unwaddr; } _c = {.unwaddr = NULL}, *c = &_c; -#endif if (atomic_read(&__stp_task_finder_state) != __STP_TF_RUNNING) { debug_task_finder_detach(); diff --git a/runtime/uprobes/uprobes_i386.c b/runtime/uprobes/uprobes_i386.c index c43f87bf..7743f400 100644 --- a/runtime/uprobes/uprobes_i386.c +++ b/runtime/uprobes/uprobes_i386.c @@ -301,9 +301,20 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, return orig_ret_addr; } +/* + * On x86_32, if a function returns a struct or union, the return + * value is copied into an area created by the caller. The address + * of this area is passed on the stack as a "hidden" first argument. + * When such a function returns, it uses a "ret $4" instruction to pop + * not only the return address but also the hidden arg. To accommodate + * such functions, we add 4 bytes of slop when predicting the return + * address. See PR #10078. + */ +#define STRUCT_RETURN_SLOP 4 + static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, struct task_struct *tsk) { - return (unsigned long) (regs->esp + 4); + return (unsigned long) (regs->esp + 4 + STRUCT_RETURN_SLOP); } diff --git a/runtime/uprobes/uprobes_x86.c b/runtime/uprobes/uprobes_x86.c index 404c9518..93331715 100644 --- a/runtime/uprobes/uprobes_x86.c +++ b/runtime/uprobes/uprobes_x86.c @@ -716,12 +716,23 @@ unsigned long arch_hijack_uret_addr(unsigned long trampoline_address, return orig_ret_addr; } +/* + * On x86_32, if a function returns a struct or union, the return + * value is copied into an area created by the caller. The address + * of this area is passed on the stack as a "hidden" first argument. + * When such a function returns, it uses a "ret $4" instruction to pop + * not only the return address but also the hidden arg. To accommodate + * such functions, we add 4 bytes of slop when predicting the return + * address. See PR #10078. + */ +#define STRUCT_RETURN_SLOP 4 + static unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, struct task_struct *tsk) { if (test_tsk_thread_flag(tsk, TIF_IA32)) - return (unsigned long) (REGS_SP + 4); + return (unsigned long) (REGS_SP + 4 + STRUCT_RETURN_SLOP); else return (unsigned long) (REGS_SP + 8); } diff --git a/runtime/uprobes2/uprobes_x86.h b/runtime/uprobes2/uprobes_x86.h index ca3f4873..a07fa0d3 100644 --- a/runtime/uprobes2/uprobes_x86.h +++ b/runtime/uprobes2/uprobes_x86.h @@ -93,11 +93,22 @@ static inline unsigned long arch_get_cur_sp(struct pt_regs *regs) return (unsigned long) regs->sp; } +/* + * On x86_32, if a function returns a struct or union, the return + * value is copied into an area created by the caller. The address + * of this area is passed on the stack as a "hidden" first argument. + * When such a function returns, it uses a "ret $4" instruction to pop + * not only the return address but also the hidden arg. To accommodate + * such functions, we add 4 bytes of slop when predicting the return + * address. See PR #10078. + */ +#define STRUCT_RETURN_SLOP 4 + static inline unsigned long arch_predict_sp_at_ret(struct pt_regs *regs, struct task_struct *tsk) { if (test_tsk_thread_flag(tsk, TIF_IA32)) - return (unsigned long) (regs->sp + 4); + return (unsigned long) (regs->sp + 4 + STRUCT_RETURN_SLOP); else return (unsigned long) (regs->sp + 8); } |