diff options
author | Dave Brolley <brolley@redhat.com> | 2009-12-21 12:42:11 -0500 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2009-12-21 12:42:11 -0500 |
commit | 25a0404570724499bcdf1ebfd9f03084c2e00137 (patch) | |
tree | f9824f847b407790ab10116c2ebcc7e664d53253 /runtime | |
parent | 08098abb6b206dc3aea984f18b5054d34e015185 (diff) | |
parent | c6fcc4c1ca5f222cf90bf3968e34a10f09b30be4 (diff) | |
download | systemtap-steved-25a0404570724499bcdf1ebfd9f03084c2e00137.tar.gz systemtap-steved-25a0404570724499bcdf1ebfd9f03084c2e00137.tar.xz systemtap-steved-25a0404570724499bcdf1ebfd9f03084c2e00137.zip |
Merge branch 'master' of ssh://sources.redhat.com/git/systemtap
Diffstat (limited to 'runtime')
-rw-r--r-- | runtime/autoconf-regset.c | 7 | ||||
-rw-r--r-- | runtime/autoconf-utrace-regset.c | 8 | ||||
-rw-r--r-- | runtime/loc2c-runtime.h | 371 | ||||
-rw-r--r-- | runtime/stack-i386.c | 2 | ||||
-rw-r--r-- | runtime/stack-x86_64.c | 2 | ||||
-rw-r--r-- | runtime/stack.c | 4 | ||||
-rw-r--r-- | runtime/sym.c | 167 | ||||
-rw-r--r-- | runtime/sym.h | 7 | ||||
-rw-r--r-- | runtime/task_finder_vma.c | 71 | ||||
-rw-r--r-- | runtime/transport/symbols.c | 2 | ||||
-rw-r--r-- | runtime/unwind.c | 31 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.c | 38 | ||||
-rw-r--r-- | runtime/uprobes/uprobes.h | 8 |
13 files changed, 569 insertions, 149 deletions
diff --git a/runtime/autoconf-regset.c b/runtime/autoconf-regset.c new file mode 100644 index 00000000..9d994b03 --- /dev/null +++ b/runtime/autoconf-regset.c @@ -0,0 +1,7 @@ +#include <linux/regset.h> + +int foobar(int n) { + const struct user_regset_view *rsv = task_user_regset_view(current); + const struct user_regset *rs = & rsv->regsets[0]; + return rsv->n + n + (rs->get)(current, rs, 0, 0, NULL, NULL); +} diff --git a/runtime/autoconf-utrace-regset.c b/runtime/autoconf-utrace-regset.c new file mode 100644 index 00000000..1728f239 --- /dev/null +++ b/runtime/autoconf-utrace-regset.c @@ -0,0 +1,8 @@ +#include <linux/tracehook.h> + +/* old rhel5 utrace regset */ +int foobar(int n) { + const struct utrace_regset_view *rsv = utrace_native_view(current); + const struct utrace_regset *rs = & rsv->regsets[0]; + return rsv->n + n + (rs->get)(current, rs, 0, 0, NULL, NULL); +} diff --git a/runtime/loc2c-runtime.h b/runtime/loc2c-runtime.h index e9e5a071..c75639ee 100644 --- a/runtime/loc2c-runtime.h +++ b/runtime/loc2c-runtime.h @@ -1,5 +1,5 @@ /* target operations - * Copyright (C) 2005 Red Hat Inc. + * Copyright (C) 2005-2009 Red Hat Inc. * Copyright (C) 2005, 2006, 2007 Intel Corporation. * Copyright (C) 2007 Quentin Barnes. * @@ -44,10 +44,10 @@ can be pasted into an identifier name. These definitions turn it into a per-register macro, defined below for machines with individually-named registers. */ -#define fetch_register(regno) \ - ((intptr_t) dwarf_register_##regno (c->regs)) -#define store_register(regno, value) \ - (dwarf_register_##regno (c->regs) = (value)) +#define k_fetch_register(regno) \ + ((intptr_t) k_dwarf_register_##regno (c->regs)) +#define k_store_register(regno, value) \ + (k_dwarf_register_##regno (c->regs) = (value)) /* The deref and store_deref macros are called to safely access addresses @@ -82,35 +82,266 @@ }) #endif +/* PR 10601: user-space (user_regset) register access. */ +#if defined(STAPCONF_REGSET) +#include <linux/regset.h> +#endif + +#if defined(STAPCONF_UTRACE_REGSET) +#include <linux/tracehook.h> +/* adapt new names to old decls */ +#define user_regset_view utrace_regset_view +#define user_regset utrace_regset +#define task_user_regset_view utrace_native_view +#endif + +#if defined(STAPCONF_REGSET) || defined(STAPCONF_UTRACE_REGSET) + +struct usr_regset_lut { + char *name; + unsigned rsn; + unsigned pos; +}; + + +/* DWARF register number -to- user_regset bank/offset mapping table. + The register numbers come from the processor-specific ELF documents. + The user-regset bank/offset values come from kernel $ARCH/include/asm/user*.h + or $ARCH/kernel/ptrace.c. */ +static const struct usr_regset_lut url_i386[] = { + { "ax", NT_PRSTATUS, 6*4 }, + { "cx", NT_PRSTATUS, 1*4 }, + { "dx", NT_PRSTATUS, 2*4 }, + { "bx", NT_PRSTATUS, 0*4 }, + { "sp", NT_PRSTATUS, 15*4 }, + { "bp", NT_PRSTATUS, 5*4 }, + { "si", NT_PRSTATUS, 3*4 }, + { "di", NT_PRSTATUS, 4*4 }, + { "ip", NT_PRSTATUS, 12*4 }, +}; + +static const struct usr_regset_lut url_x86_64[] = { + { "rax", NT_PRSTATUS, 10*8 }, + { "rdx", NT_PRSTATUS, 12*8 }, + { "rcx", NT_PRSTATUS, 11*8 }, + { "rbx", NT_PRSTATUS, 5*8 }, + { "rsi", NT_PRSTATUS, 13*8 }, + { "rdi", NT_PRSTATUS, 14*8 }, + { "rbp", NT_PRSTATUS, 4*8 }, + { "rsp", NT_PRSTATUS, 19*8 }, + { "r8", NT_PRSTATUS, 9*8 }, + { "r9", NT_PRSTATUS, 8*8 }, + { "r10", NT_PRSTATUS, 7*8 }, + { "r11", NT_PRSTATUS, 6*8 }, + { "r12", NT_PRSTATUS, 3*8 }, + { "r13", NT_PRSTATUS, 2*8 }, + { "r14", NT_PRSTATUS, 1*8 }, + { "r15", NT_PRSTATUS, 0*8 }, + { "rip", NT_PRSTATUS, 16*8 }, + /* XXX: SSE registers %xmm0-%xmm7 */ + /* XXX: SSE2 registers %xmm8-%xmm15 */ + /* XXX: FP registers %st0-%st7 */ + /* XXX: MMX registers %mm0-%mm7 */ +}; +/* XXX: insert other architectures here. */ + + +static u32 ursl_fetch32 (const struct usr_regset_lut* lut, unsigned lutsize, int e_machine, unsigned regno) +{ + u32 value = ~0; + const struct user_regset_view *rsv = task_user_regset_view(current); + unsigned rsi; + int rc; + unsigned rsn; + unsigned pos; + unsigned count; + + WARN_ON (!rsv); + if (!rsv) goto out; + WARN_ON (regno >= lutsize); + if (regno >= lutsize) goto out; + if (rsv->e_machine != e_machine) goto out; + + rsn = lut[regno].rsn; + pos = lut[regno].pos; + count = sizeof(value); + + for (rsi=0; rsi<rsv->n; rsi++) + if (rsv->regsets[rsi].core_note_type == rsn) + { + const struct user_regset *rs = & rsv->regsets[rsi]; + rc = (rs->get)(current, rs, pos, count, & value, NULL); + WARN_ON (rc); + /* success */ + goto out; + } + WARN_ON (1); /* did not find appropriate regset! */ + + out: + return value; +} + + +static void ursl_store32 (const struct usr_regset_lut* lut,unsigned lutsize, int e_machine, unsigned regno, u32 value) +{ + const struct user_regset_view *rsv = task_user_regset_view(current); + unsigned rsi; + int rc; + unsigned rsn; + unsigned pos; + unsigned count; + + WARN_ON (!rsv); + if (!rsv) goto out; + WARN_ON (regno >= lutsize); + if (regno >= lutsize) goto out; + if (rsv->e_machine != e_machine) goto out; + + rsn = lut[regno].rsn; + pos = lut[regno].pos; + count = sizeof(value); + + for (rsi=0; rsi<rsv->n; rsi++) + if (rsv->regsets[rsi].core_note_type == rsn) + { + const struct user_regset *rs = & rsv->regsets[rsi]; + rc = (rs->set)(current, rs, pos, count, & value, NULL); + WARN_ON (rc); + /* success */ + goto out; + } + WARN_ON (1); /* did not find appropriate regset! */ + + out: + return; +} + + +static u64 ursl_fetch64 (const struct usr_regset_lut* lut, unsigned lutsize, int e_machine, unsigned regno) +{ + u64 value = ~0; + const struct user_regset_view *rsv = task_user_regset_view(current); + unsigned rsi; + int rc; + unsigned rsn; + unsigned pos; + unsigned count; + + if (!rsv) goto out; + if (regno >= lutsize) goto out; + if (rsv->e_machine != e_machine) goto out; + + rsn = lut[regno].rsn; + pos = lut[regno].pos; + count = sizeof(value); + + for (rsi=0; rsi<rsv->n; rsi++) + if (rsv->regsets[rsi].core_note_type == rsn) + { + const struct user_regset *rs = & rsv->regsets[rsi]; + rc = (rs->get)(current, rs, pos, count, & value, NULL); + if (rc) + goto out; + /* success */ + return value; + } + out: + printk (KERN_WARNING "process %d mach %d regno %d not available for fetch.\n", current->tgid, e_machine, regno); + return value; +} + + +static void ursl_store64 (const struct usr_regset_lut* lut,unsigned lutsize, int e_machine, unsigned regno, u64 value) +{ + const struct user_regset_view *rsv = task_user_regset_view(current); + unsigned rsi; + int rc; + unsigned rsn; + unsigned pos; + unsigned count; + + WARN_ON (!rsv); + if (!rsv) goto out; + WARN_ON (regno >= lutsize); + if (regno >= lutsize) goto out; + if (rsv->e_machine != e_machine) goto out; + + rsn = lut[regno].rsn; + pos = lut[regno].pos; + count = sizeof(value); + + for (rsi=0; rsi<rsv->n; rsi++) + if (rsv->regsets[rsi].core_note_type == rsn) + { + const struct user_regset *rs = & rsv->regsets[rsi]; + rc = (rs->set)(current, rs, pos, count, & value, NULL); + if (rc) + goto out; + /* success */ + return; + } + + out: + printk (KERN_WARNING "process %d mach %d regno %d not available for store.\n", current->tgid, e_machine, regno); + return; +} + + +#if defined (__i386__) + +#define u_fetch_register(regno) ursl_fetch32(url_i386, ARRAY_SIZE(url_i386), EM_386, regno) +#define u_store_register(regno,value) ursl_store32(url_i386, ARRAY_SIZE(url_i386), EM_386, regno, value) + +#elif defined (__x86_64__) + +#define u_fetch_register(regno) (_stp_probing_32bit_app(c->regs) ? ursl_fetch32(url_i386, ARRAY_SIZE(url_i386), EM_386, regno) : ursl_fetch64(url_x86_64, ARRAY_SIZE(url_x86_64), EM_X86_64, regno)) +#define u_store_register(regno,value) (_stp_probing_32bit_app(c->regs) ? ursl_store2(url_i386, ARRAY_SIZE(url_i386), EM_386, regno, value) : ursl_store64(url_x86_64, ARRAY_SIZE(url_x86_64), EM_X86_64, regno, value)) + +#else + +/* Some other architecture; downgrade to kernel register access. */ +#define u_fetch_register(regno) k_fetch_register(regno) +#define u_store_register(regno,value) k_store_register(regno,value) + +#endif + + +#else /* ! STAPCONF_REGSET */ +/* Downgrade to kernel register access. */ +#define u_fetch_register(regno) k_fetch_register(regno) +#define u_store_register(regno,value) k_store_register(regno,value) +#endif + + #if defined (STAPCONF_X86_UNIREGS) && defined (__i386__) -#define dwarf_register_0(regs) regs->ax -#define dwarf_register_1(regs) regs->cx -#define dwarf_register_2(regs) regs->dx -#define dwarf_register_3(regs) regs->bx -#define dwarf_register_4(regs) ((long) ®s->sp) -#define dwarf_register_5(regs) regs->bp -#define dwarf_register_6(regs) regs->si -#define dwarf_register_7(regs) regs->di +#define k_dwarf_register_0(regs) regs->ax +#define k_dwarf_register_1(regs) regs->cx +#define k_dwarf_register_2(regs) regs->dx +#define k_dwarf_register_3(regs) regs->bx +#define k_dwarf_register_4(regs) ((long) ®s->sp) +#define k_dwarf_register_5(regs) regs->bp +#define k_dwarf_register_6(regs) regs->si +#define k_dwarf_register_7(regs) regs->di #elif defined (STAPCONF_X86_UNIREGS) && defined (__x86_64__) -#define dwarf_register_0(regs) regs->ax -#define dwarf_register_1(regs) regs->dx -#define dwarf_register_2(regs) regs->cx -#define dwarf_register_3(regs) regs->bx -#define dwarf_register_4(regs) regs->si -#define dwarf_register_5(regs) regs->di -#define dwarf_register_6(regs) regs->bp -#define dwarf_register_7(regs) regs->sp -#define dwarf_register_8(regs) regs->r8 -#define dwarf_register_9(regs) regs->r9 -#define dwarf_register_10(regs) regs->r10 -#define dwarf_register_11(regs) regs->r11 -#define dwarf_register_12(regs) regs->r12 -#define dwarf_register_13(regs) regs->r13 -#define dwarf_register_14(regs) regs->r14 -#define dwarf_register_15(regs) regs->r15 +#define k_dwarf_register_0(regs) regs->ax +#define k_dwarf_register_1(regs) regs->dx +#define k_dwarf_register_2(regs) regs->cx +#define k_dwarf_register_3(regs) regs->bx +#define k_dwarf_register_4(regs) regs->si +#define k_dwarf_register_5(regs) regs->di +#define k_dwarf_register_6(regs) regs->bp +#define k_dwarf_register_7(regs) regs->sp +#define k_dwarf_register_8(regs) regs->r8 +#define k_dwarf_register_9(regs) regs->r9 +#define k_dwarf_register_10(regs) regs->r10 +#define k_dwarf_register_11(regs) regs->r11 +#define k_dwarf_register_12(regs) regs->r12 +#define k_dwarf_register_13(regs) regs->r13 +#define k_dwarf_register_14(regs) regs->r14 +#define k_dwarf_register_15(regs) regs->r15 #elif defined __i386__ @@ -120,60 +351,60 @@ For a kernel mode trap, the interrupted state's esp is actually an address inside where the `struct pt_regs' on the kernel trap stack points. */ -#define dwarf_register_0(regs) regs->eax -#define dwarf_register_1(regs) regs->ecx -#define dwarf_register_2(regs) regs->edx -#define dwarf_register_3(regs) regs->ebx -#define dwarf_register_4(regs) (user_mode(regs) ? regs->esp : (long)®s->esp) -#define dwarf_register_5(regs) regs->ebp -#define dwarf_register_6(regs) regs->esi -#define dwarf_register_7(regs) regs->edi +#define k_dwarf_register_0(regs) regs->eax +#define k_dwarf_register_1(regs) regs->ecx +#define k_dwarf_register_2(regs) regs->edx +#define k_dwarf_register_3(regs) regs->ebx +#define k_dwarf_register_4(regs) (user_mode(regs) ? regs->esp : (long)®s->esp) +#define k_dwarf_register_5(regs) regs->ebp +#define k_dwarf_register_6(regs) regs->esi +#define k_dwarf_register_7(regs) regs->edi #elif defined __ia64__ -#undef fetch_register -#undef store_register +#undef k_fetch_register +#undef k_store_register -#define fetch_register(regno) ia64_fetch_register(regno, c->regs, &c->unwaddr) -#define store_register(regno,value) ia64_store_register(regno, c->regs, value) +#define k_fetch_register(regno) ia64_fetch_register(regno, c->regs, &c->unwaddr) +#define k_store_register(regno,value) ia64_store_register(regno, c->regs, value) #elif defined __x86_64__ -#define dwarf_register_0(regs) regs->rax -#define dwarf_register_1(regs) regs->rdx -#define dwarf_register_2(regs) regs->rcx -#define dwarf_register_3(regs) regs->rbx -#define dwarf_register_4(regs) regs->rsi -#define dwarf_register_5(regs) regs->rdi -#define dwarf_register_6(regs) regs->rbp -#define dwarf_register_7(regs) regs->rsp -#define dwarf_register_8(regs) regs->r8 -#define dwarf_register_9(regs) regs->r9 -#define dwarf_register_10(regs) regs->r10 -#define dwarf_register_11(regs) regs->r11 -#define dwarf_register_12(regs) regs->r12 -#define dwarf_register_13(regs) regs->r13 -#define dwarf_register_14(regs) regs->r14 -#define dwarf_register_15(regs) regs->r15 +#define k_dwarf_register_0(regs) regs->rax +#define k_dwarf_register_1(regs) regs->rdx +#define k_dwarf_register_2(regs) regs->rcx +#define k_dwarf_register_3(regs) regs->rbx +#define k_dwarf_register_4(regs) regs->rsi +#define k_dwarf_register_5(regs) regs->rdi +#define k_dwarf_register_6(regs) regs->rbp +#define k_dwarf_register_7(regs) regs->rsp +#define k_dwarf_register_8(regs) regs->r8 +#define k_dwarf_register_9(regs) regs->r9 +#define k_dwarf_register_10(regs) regs->r10 +#define k_dwarf_register_11(regs) regs->r11 +#define k_dwarf_register_12(regs) regs->r12 +#define k_dwarf_register_13(regs) regs->r13 +#define k_dwarf_register_14(regs) regs->r14 +#define k_dwarf_register_15(regs) regs->r15 #elif defined __powerpc__ -#undef fetch_register -#undef store_register -#define fetch_register(regno) ((intptr_t) c->regs->gpr[regno]) -#define store_register(regno,value) (c->regs->gpr[regno] = (value)) +#undef k_fetch_register +#undef k_store_register +#define k_fetch_register(regno) ((intptr_t) c->regs->gpr[regno]) +#define k_store_register(regno,value) (c->regs->gpr[regno] = (value)) #elif defined (__arm__) -#undef fetch_register -#undef store_register -#define fetch_register(regno) ((long) c->regs->uregs[regno]) -#define store_register(regno,value) (c->regs->uregs[regno] = (value)) +#undef k_fetch_register +#undef k_store_register +#define k_fetch_register(regno) ((long) c->regs->uregs[regno]) +#define k_store_register(regno,value) (c->regs->uregs[regno] = (value)) #elif defined (__s390__) || defined (__s390x__) -#undef fetch_register -#undef store_register -#define fetch_register(regno) ((intptr_t) c->regs->gprs[regno]) -#define store_register(regno,value) (c->regs->gprs[regno] = (value)) +#undef k_fetch_register +#undef k_store_register +#define k_fetch_register(regno) ((intptr_t) c->regs->gprs[regno]) +#define k_store_register(regno,value) (c->regs->gprs[regno] = (value)) #endif @@ -228,6 +459,7 @@ } \ }) + extern void __deref_bad(void); extern void __store_deref_bad(void); @@ -746,6 +978,7 @@ extern void __store_deref_bad(void); #endif /* STAPCONF_PROBE_KERNEL */ + #define deref_string(dst, addr, maxbytes) \ ({ \ uintptr_t _addr; \ diff --git a/runtime/stack-i386.c b/runtime/stack-i386.c index 66d892d3..d10aeb2f 100644 --- a/runtime/stack-i386.c +++ b/runtime/stack-i386.c @@ -63,7 +63,7 @@ static void __stp_stack_print (struct pt_regs *regs, int verbose, int levels, while (levels && (tsk || !arch_unw_user_mode(&info))) { int ret = unwind(&info, tsk); -#if defined(UPROBES_API_VERSION) && UPROBES_API_VERSION > 1 +#ifdef CONFIG_UTRACE unsigned long maybe_pc = 0; if (ri) { maybe_pc = uprobe_get_pc(ri, UNW_PC(&info), diff --git a/runtime/stack-x86_64.c b/runtime/stack-x86_64.c index d6c63412..030e5ef1 100644 --- a/runtime/stack-x86_64.c +++ b/runtime/stack-x86_64.c @@ -38,7 +38,7 @@ static void __stp_stack_print(struct pt_regs *regs, int verbose, int levels, while (levels && (tsk || !arch_unw_user_mode(&info))) { int ret = unwind(&info, tsk); -#if defined(UPROBES_API_VERSION) && UPROBES_API_VERSION > 1 +#ifdef CONFIG_UTRACE unsigned long maybe_pc = 0; if (ri) { maybe_pc = uprobe_get_pc(ri, UNW_PC(&info), diff --git a/runtime/stack.c b/runtime/stack.c index 4dd1dca3..50dde6e1 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -133,13 +133,15 @@ static void _stp_stack_print(struct pt_regs *regs, int verbose, struct kretprobe } _stp_symbol_print((unsigned long)_stp_ret_addr_r(pi)); } else if (ri) { +#ifdef CONFIG_UTRACE /* as a proxy for presence of uprobes */ if (verbose == SYM_VERBOSE_FULL) { _stp_print("Returning from: "); - _stp_usymbol_print(ri->rp->u.vaddr, tsk); + _stp_usymbol_print(ri->rp->u.vaddr, tsk); /* otherwise this dereference fails */ _stp_print("\nReturning to : "); _stp_usymbol_print(ri->ret_addr, tsk); } else _stp_func_print(ri->ret_addr, verbose, 0, tsk); +#endif } else { _stp_print_char(' '); if (tsk) diff --git a/runtime/sym.c b/runtime/sym.c index cd0c8a71..06691dc9 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -19,6 +19,22 @@ /* Callback that needs to be registered (in session.unwindsyms_modules) for every user task path for which we might need symbols or unwind info. */ +static int _stp_tf_exec_cb(struct stap_task_finder_target *tgt, + struct task_struct *tsk, + int register_p, + int process_p) +{ +#ifdef DEBUG_TASK_FINDER_VMA + _stp_dbug(__FUNCTION__, __LINE__, + "tsk %d:%d , register_p: %d, process_p: %d\n", + tsk->pid, tsk->tgid, register_p, process_p); +#endif + if (process_p && ! register_p) + stap_drop_vma_maps(tsk); + + return 0; +} + static int _stp_tf_mmap_cb(struct stap_task_finder_target *tgt, struct task_struct *tsk, char *path, @@ -42,40 +58,22 @@ static int _stp_tf_mmap_cb(struct stap_task_finder_target *tgt, if (strcmp(path, _stp_modules[i]->path) == 0) { #ifdef DEBUG_TASK_FINDER_VMA - _stp_dbug(__FUNCTION__, __LINE__, - "vm_cb: matched path %s to module\n", - path); + _stp_dbug(__FUNCTION__, __LINE__, + "vm_cb: matched path %s to module (for sec: %s)\n", + path, _stp_modules[i]->sections[0].name); #endif - module = _stp_modules[i]; - // cheat... - // We are abusing the "first" section address - // here to indicate where the module (actually - // first segment) is loaded (which is why we - // are ignoring the offset). It would be good - // to redesign the stp_module/stp_section - // data structures to better align with the - // actual memory mappings we are interested - // in (especially the "section" naming is - // slightly confusing since what we really - // seem to mean are elf segments (which can - // contain multiple elf sections). PR11015. - if (strcmp(".dynamic", - module->sections[0].name) == 0) - { - if (module->sections[0].addr == 0) - module->sections[0].addr = addr; - else if (module->sections[0].addr != addr) - _stp_error ("Reloaded module '%s'" - " at 0x%lx, was 0x%lx\n", - path, addr, - module->sections[0].addr); - } - break; + module = _stp_modules[i]; + /* XXX We really only need to register .dynamic + sections, but .absolute exes are also necessary + atm. */ + return stap_add_vma_map_info(tsk->group_leader, + addr, + addr + length, + offset, + module); } } } - stap_add_vma_map_info(tsk->group_leader, addr, addr + length, offset, - module); return 0; } @@ -84,15 +82,25 @@ static int _stp_tf_munmap_cb(struct stap_task_finder_target *tgt, unsigned long addr, unsigned long length) { + /* Unconditionally remove vm map info, ignore if not present. */ stap_remove_vma_map_info(tsk->group_leader, addr, addr + length, 0); return 0; } -/* XXX: this needs to be address-space-specific. */ -static unsigned long _stp_module_relocate(const char *module, const char *section, unsigned long offset) +/* Returns absolute address of offset into module/section for given task. + If tsk == NULL module/section is assumed to be absolute/static already + (e.g. kernel, kernel-modules and static executables). Returns zero when + module and section couldn't be found (aren't in memory yet). */ +static unsigned long _stp_module_relocate(const char *module, + const char *section, + unsigned long offset, + struct task_struct *tsk) { + /* XXX This doesn't look thread safe XXX */ static struct _stp_module *last = NULL; static struct _stp_section *last_sec; + static struct task_struct *last_tsk; + static unsigned long last_offset; unsigned i, j; /* if module is -1, we invalidate last. _stp_del_module calls this when modules are deleted. */ @@ -110,8 +118,10 @@ static unsigned long _stp_module_relocate(const char *module, const char *sectio /* Most likely our relocation is in the same section of the same module as the last. */ if (last) { - if (!strcmp(module, last->name) && !strcmp(section, last_sec->name)) { - offset += last_sec->addr; + if (!strcmp(module, last->name) + && !strcmp(section, last_sec->name) + && tsk == last_tsk) { + offset += last_offset; dbug_sym(1, "cached address=%lx\n", offset); return offset; } @@ -124,11 +134,33 @@ static unsigned long _stp_module_relocate(const char *module, const char *sectio for (j = 0; j < last->num_sections; j++) { last_sec = &last->sections[j]; if (!strcmp(section, last_sec->name)) { - - if (last_sec->addr == 0) /* module/section not in memory */ - continue; - - offset += last_sec->addr; + /* mod and sec name match. tsk should match dynamic/static. */ + if (last_sec->static_addr != 0) { + last_offset = last_sec->static_addr; + } else { + if (!tsk) { /* static section, not in memory yet? */ + if (strcmp(".dynamic", section) == 0) + _stp_error("internal error, _stp_module_relocate '%s' " + "section '%s', should not be tsk dynamic\n", + module, section); + last = NULL; + return 0; + } else { /* dynamic section, look up through tsk vma. */ + if (strcmp(".dynamic", last_sec->name) != 0) { + _stp_error("internal error, _stp_module_relocate '%s' " + "section '%s', should not be tsk dynamic\n", + module, section); + return 0; + } + if (stap_find_vma_map_info_user(tsk->group_leader, last, + &last_offset, NULL, + NULL) != 0) { + last = NULL; + return 0; + } + } + } + offset += last_offset; dbug_sym(1, "address=%lx\n", offset); return offset; } @@ -140,11 +172,12 @@ static unsigned long _stp_module_relocate(const char *module, const char *sectio } /* Return module owner and, if sec != NULL, fills in closest section - of the address if found, return NULL otherwise. - XXX: needs to be address-space-specific. */ + of the address if found, return NULL otherwise. Fills in rel_addr + (addr relative to closest section) when given. */ static struct _stp_module *_stp_mod_sec_lookup(unsigned long addr, struct task_struct *task, - struct _stp_section **sec) + struct _stp_section **sec, + unsigned long *rel_addr) { void *user = NULL; unsigned midx = 0; @@ -160,11 +193,21 @@ static struct _stp_module *_stp_mod_sec_lookup(unsigned long addr, { struct _stp_module *m = (struct _stp_module *)user; if (sec) - *sec = &m->sections[0]; // XXX check actual section and relocate + *sec = &m->sections[0]; // dynamic user modules have one section. + if (rel_addr) + { + /* XXX .absolute sections really shouldn't be here... */ + if (strcmp(".dynamic", m->sections[0].name) == 0) + *rel_addr = addr - vm_start; + else + *rel_addr = addr; + } dbug_sym(1, "found section %s in module %s at 0x%lx\n", m->sections[0].name, m->name, vm_start); return m; } + /* XXX should really not fallthrough, but sometimes current is passed + when it shouldn't - see probefunc() for example. */ } for (midx = 0; midx < _stp_num_modules; midx++) @@ -174,12 +217,14 @@ static struct _stp_module *_stp_mod_sec_lookup(unsigned long addr, { unsigned long sec_addr; unsigned long sec_size; - sec_addr = _stp_modules[midx]->sections[secidx].addr; + sec_addr = _stp_modules[midx]->sections[secidx].static_addr; sec_size = _stp_modules[midx]->sections[secidx].size; if (addr >= sec_addr && addr < sec_addr + sec_size) { if (sec) *sec = & _stp_modules[midx]->sections[secidx]; + if (rel_addr) + *rel_addr = addr - sec_addr; return _stp_modules[midx]; } } @@ -188,7 +233,6 @@ static struct _stp_module *_stp_mod_sec_lookup(unsigned long addr, } -/* XXX: needs to be address-space-specific. */ static const char *_stp_kallsyms_lookup(unsigned long addr, unsigned long *symbolsize, unsigned long *offset, const char **modname, @@ -200,13 +244,14 @@ static const char *_stp_kallsyms_lookup(unsigned long addr, unsigned long *symbo struct _stp_section *sec = NULL; struct _stp_symbol *s = NULL; unsigned end, begin = 0; + unsigned long rel_addr = 0; - m = _stp_mod_sec_lookup(addr, task, &sec); + m = _stp_mod_sec_lookup(addr, task, &sec, &rel_addr); if (unlikely (m == NULL || sec == NULL)) return NULL; /* NB: relativize the address to the section. */ - addr -= sec->addr; + addr = rel_addr; end = sec->num_symbols; /* binary search for symbols within the module */ @@ -268,9 +313,9 @@ static int _stp_module_check(void) /* notes end address */ if (!strcmp(m->name, "kernel")) { notes_addr = _stp_module_relocate("kernel", - "_stext", m->build_id_offset); + "_stext", m->build_id_offset, NULL); base_addr = _stp_module_relocate("kernel", - "_stext", 0); + "_stext", 0, NULL); } else { notes_addr = m->notes_sect + m->build_id_offset; base_addr = m->notes_sect; @@ -446,14 +491,20 @@ static void _stp_sym_init(void) static struct stap_task_finder_target vmcb = { // NB: no .pid, no .procname filters here. // This means that we get a system-wide mmap monitoring - // widget while the script is running. (The system-wideness may - // be restricted by stap -c or -x.) But this seems to - // be necessary if we want to to stack tracebacks through arbitrary - // shared libraries. XXX: There may be an optimization opportunity - // for executables (for which the main task-finder callback should be - // sufficient). + // widget while the script is running. (The + // system-wideness may be restricted by stap -c or + // -x.) But this seems to be necessary if we want to + // to stack tracebacks through arbitrary shared libraries. + // + // XXX: There may be an optimization opportunity + // for executables (for which the main task-finder + // callback should be sufficient). + .pid = 0, + .procname = NULL, + .callback = &_stp_tf_exec_cb, .mmap_callback = &_stp_tf_mmap_cb, .munmap_callback = &_stp_tf_munmap_cb, + .mprotect_callback = NULL }; if (! initialized) { int rc; @@ -462,8 +513,10 @@ static void _stp_sym_init(void) #ifdef DEBUG_TASK_FINDER_VMA _stp_dbug(__FUNCTION__, __LINE__, "registered vmcb"); #endif - (void) rc; // XXX - initialized = 1; + if (rc != 0) + _stp_error("Couldn't register task finder target: %d\n", rc); + else + initialized = 1; } #endif } diff --git a/runtime/sym.h b/runtime/sym.h index 9f2bdfd0..ce6ab736 100644 --- a/runtime/sym.h +++ b/runtime/sym.h @@ -17,7 +17,7 @@ struct _stp_symbol { struct _stp_section { const char *name; - unsigned long addr; /* XXX: belongs in per-address-space tables */ + unsigned long static_addr; /* XXX non-null if everywhere the same. */ unsigned long size; /* length of the address space module covers. */ struct _stp_symbol *symbols; /* ordered by address */ unsigned num_symbols; @@ -70,7 +70,10 @@ static unsigned long _stp_kretprobe_trampoline; _stp_sym_init () should track vma maps. */ static char _stp_need_vma_tracker; -static unsigned long _stp_module_relocate (const char *module, const char *section, unsigned long offset); +static unsigned long _stp_module_relocate (const char *module, + const char *section, + unsigned long offset, + struct task_struct *tsk); static struct _stp_module *_stp_get_unwind_info (unsigned long addr); #endif /* _STP_SYM_H_ */ diff --git a/runtime/task_finder_vma.c b/runtime/task_finder_vma.c index ed9c6f4f..9a32323f 100644 --- a/runtime/task_finder_vma.c +++ b/runtime/task_finder_vma.c @@ -270,6 +270,7 @@ stap_add_vma_map_info(struct task_struct *tsk, unsigned long vm_start, // Remove the vma entry from the vma hash table. +// Returns -ESRCH if the entry isn't present. static int stap_remove_vma_map_info(struct task_struct *tsk, unsigned long vm_start, unsigned long vm_end, unsigned long vm_pgoff) @@ -277,6 +278,7 @@ stap_remove_vma_map_info(struct task_struct *tsk, unsigned long vm_start, struct hlist_head *head; struct hlist_node *node; struct __stp_tf_vma_entry *entry; + int rc = -ESRCH; // Take a write lock since we are most likely going to delete // after reading. @@ -286,13 +288,15 @@ stap_remove_vma_map_info(struct task_struct *tsk, unsigned long vm_start, if (entry != NULL) { hlist_del(&entry->hlist); __stp_tf_vma_put_free_entry(entry); + rc = 0; } write_unlock_irqrestore(&__stp_tf_vma_lock, flags); - return 0; + return rc; } -// Finds vma info if the vma is present in the vma map hash table. -// Returns ESRCH if not present. The __stp_tf_vma_lock must *not* be +// Finds vma info if the vma is present in the vma map hash table for +// a given task and address (between vm_start and vm_end). +// Returns -ESRCH if not present. The __stp_tf_vma_lock must *not* be // locked before calling this function. static int stap_find_vma_map_info(struct task_struct *tsk, unsigned long vm_addr, @@ -303,7 +307,7 @@ stap_find_vma_map_info(struct task_struct *tsk, unsigned long vm_addr, struct hlist_node *node; struct __stp_tf_vma_entry *entry; struct __stp_tf_vma_entry *found_entry = NULL; - int rc = ESRCH; + int rc = -ESRCH; unsigned long flags; read_lock_irqsave(&__stp_tf_vma_lock, flags); @@ -330,3 +334,62 @@ stap_find_vma_map_info(struct task_struct *tsk, unsigned long vm_addr, read_unlock_irqrestore(&__stp_tf_vma_lock, flags); return rc; } + +// Finds vma info if the vma is present in the vma map hash table for +// a given task with the given user handle. +// Returns -ESRCH if not present. The __stp_tf_vma_lock must *not* be +// locked before calling this function. +static int +stap_find_vma_map_info_user(struct task_struct *tsk, void *user, + unsigned long *vm_start, unsigned long *vm_end, + unsigned long *vm_pgoff) +{ + struct hlist_head *head; + struct hlist_node *node; + struct __stp_tf_vma_entry *entry; + struct __stp_tf_vma_entry *found_entry = NULL; + int rc = -ESRCH; + + unsigned long flags; + read_lock_irqsave(&__stp_tf_vma_lock, flags); + head = &__stp_tf_vma_map[__stp_tf_vma_map_hash(tsk)]; + hlist_for_each_entry(entry, node, head, hlist) { + if (tsk->pid == entry->pid + && user == entry->user) { + found_entry = entry; + break; + } + } + if (found_entry != NULL) { + if (vm_start != NULL) + *vm_start = found_entry->vm_start; + if (vm_end != NULL) + *vm_end = found_entry->vm_end; + if (vm_pgoff != NULL) + *vm_pgoff = found_entry->vm_pgoff; + rc = 0; + } + read_unlock_irqrestore(&__stp_tf_vma_lock, flags); + return rc; +} + +static int +stap_drop_vma_maps(struct task_struct *tsk) +{ + struct hlist_head *head; + struct hlist_node *node; + struct hlist_node *n; + struct __stp_tf_vma_entry *entry; + + unsigned long flags; + write_lock_irqsave(&__stp_tf_vma_lock, flags); + head = &__stp_tf_vma_map[__stp_tf_vma_map_hash(tsk)]; + hlist_for_each_entry_safe(entry, node, n, head, hlist) { + if (tsk->pid == entry->pid) { + hlist_del(&entry->hlist); + __stp_tf_vma_put_free_entry(entry); + } + } + write_unlock_irqrestore(&__stp_tf_vma_lock, flags); + return 0; +} diff --git a/runtime/transport/symbols.c b/runtime/transport/symbols.c index a214d1f2..e2f9fd65 100644 --- a/runtime/transport/symbols.c +++ b/runtime/transport/symbols.c @@ -61,7 +61,7 @@ static void _stp_do_relocation(const char __user *buf, size_t count) continue; else { - _stp_modules[mi]->sections[si].addr = msg.address; + _stp_modules[mi]->sections[si].static_addr = msg.address; break; } } /* loop over sections */ diff --git a/runtime/unwind.c b/runtime/unwind.c index 7607770e..c8e3580d 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -496,7 +496,7 @@ static char *_stp_eh_enc_name(signed type) // and the elfutils base relocation done during loading of the .dwarf_frame // in translate.cxx. static unsigned long -adjustStartLoc (unsigned long startLoc, +adjustStartLoc (unsigned long startLoc, struct task_struct *tsk, struct _stp_module *m, struct _stp_section *s, unsigned ptrType, int is_ehframe) @@ -521,10 +521,14 @@ adjustStartLoc (unsigned long startLoc, return startLoc; } - if (strcmp (s->name, ".dynamic") == 0) - return startLoc + s->addr; + if (strcmp (s->name, ".dynamic") == 0) { + unsigned long vm_addr; + if (stap_find_vma_map_info_user(tsk->group_leader, m, + &vm_addr, NULL, NULL) == 0) + return startLoc + vm_addr; + } - startLoc = _stp_module_relocate (m->name, s->name, startLoc); + startLoc = _stp_module_relocate (m->name, s->name, startLoc, tsk); startLoc -= m->dwarf_module_base; return startLoc; } @@ -532,7 +536,7 @@ adjustStartLoc (unsigned long startLoc, /* If we previously created an unwind header, then use it now to binary search */ /* for the FDE corresponding to pc. XXX FIXME not currently supported. */ -static u32 *_stp_search_unwind_hdr(unsigned long pc, +static u32 *_stp_search_unwind_hdr(unsigned long pc, struct task_struct *tsk, struct _stp_module *m, struct _stp_section *s) { @@ -581,7 +585,7 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, do { const u8 *cur = ptr + (num / 2) * (2 * tableSize); startLoc = read_pointer(&cur, cur + tableSize, hdr[3]); - startLoc = adjustStartLoc(startLoc, m, s, hdr[3], 1); + startLoc = adjustStartLoc(startLoc, tsk, m, s, hdr[3], 1); if (pc < startLoc) num /= 2; else { @@ -590,7 +594,7 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, } } while (startLoc && num > 1); - if (num == 1 && (startLoc = adjustStartLoc(read_pointer(&ptr, ptr + tableSize, hdr[3]), m, s, hdr[3], 1)) != 0 && pc >= startLoc) + if (num == 1 && (startLoc = adjustStartLoc(read_pointer(&ptr, ptr + tableSize, hdr[3]), tsk, m, s, hdr[3], 1)) != 0 && pc >= startLoc) fde = (void *)read_pointer(&ptr, ptr + tableSize, hdr[3]); dbug_unwind(1, "returning fde=%lx startLoc=%lx", (unsigned long) fde, startLoc); @@ -601,6 +605,7 @@ static u32 *_stp_search_unwind_hdr(unsigned long pc, * number in case of an error. A positive return means unwinding is finished; * don't try to fallback to dumping addresses on the stack. */ static int unwind_frame(struct unwind_frame_info *frame, + struct task_struct *tsk, struct _stp_module *m, struct _stp_section *s, void *table, uint32_t table_len, int is_ehframe) { @@ -619,7 +624,7 @@ static int unwind_frame(struct unwind_frame_info *frame, goto err; } - fde = _stp_search_unwind_hdr(pc, m, s); + fde = _stp_search_unwind_hdr(pc, tsk, m, s); dbug_unwind(1, "%s: fde=%lx\n", m->name, (unsigned long) fde); /* found the fde, now set startLoc and endLoc */ @@ -629,7 +634,7 @@ static int unwind_frame(struct unwind_frame_info *frame, ptr = (const u8 *)(fde + 2); ptrType = fde_pointer_type(cie, table, table_len); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); - startLoc = adjustStartLoc(startLoc, m, s, ptrType, is_ehframe); + startLoc = adjustStartLoc(startLoc, tsk, m, s, ptrType, is_ehframe); dbug_unwind(2, "startLoc=%lx, ptrType=%s\n", startLoc, _stp_eh_enc_name(ptrType)); if (!(ptrType & DW_EH_PE_indirect)) @@ -660,7 +665,7 @@ static int unwind_frame(struct unwind_frame_info *frame, ptr = (const u8 *)(fde + 2); startLoc = read_pointer(&ptr, (const u8 *)(fde + 1) + *fde, ptrType); - startLoc = adjustStartLoc(startLoc, m, s, ptrType, is_ehframe); + startLoc = adjustStartLoc(startLoc, tsk, m, s, ptrType, is_ehframe); dbug_unwind(2, "startLoc=%lx, ptrType=%s\n", startLoc, _stp_eh_enc_name(ptrType)); if (!startLoc) continue; @@ -902,18 +907,18 @@ static int unwind(struct unwind_frame_info *frame, struct task_struct *tsk) if (UNW_PC(frame) == 0) return -EINVAL; - m = _stp_mod_sec_lookup (pc, tsk, &s); + m = _stp_mod_sec_lookup (pc, tsk, &s, NULL); if (unlikely(m == NULL)) { dbug_unwind(1, "No module found for pc=%lx", pc); return -EINVAL; } dbug_unwind(1, "trying debug_frame\n"); - res = unwind_frame (frame, m, s, m->debug_frame, + res = unwind_frame (frame, tsk, m, s, m->debug_frame, m->debug_frame_len, 0); if (res != 0) { dbug_unwind(1, "debug_frame failed: %d, trying eh_frame\n", res); - res = unwind_frame (frame, m, s, m->eh_frame, + res = unwind_frame (frame, tsk, m, s, m->eh_frame, m->eh_frame_len, 1); } diff --git a/runtime/uprobes/uprobes.c b/runtime/uprobes/uprobes.c index 60c84509..cdb98707 100644 --- a/runtime/uprobes/uprobes.c +++ b/runtime/uprobes/uprobes.c @@ -2596,6 +2596,44 @@ static void uretprobe_set_trampoline(struct uprobe_process *uproc, } } +unsigned long uprobe_get_pc(struct uretprobe_instance *ri, unsigned long pc, + unsigned long sp) +{ + struct uretprobe *rp; + struct uprobe_kimg *uk; + struct uprobe_process *uproc; + unsigned long trampoline_addr; + struct hlist_node *r; + struct uretprobe_instance *ret_inst; + + if (!ri) + return 0; + rp = ri->rp; + uk = (struct uprobe_kimg *)rp->u.kdata; + if (!uk) + return 0; + uproc = uk->ppt->uproc; + if (IS_ERR(uproc->uretprobe_trampoline_addr)) + return pc; + trampoline_addr = (unsigned long)uproc->uretprobe_trampoline_addr; + if (pc != trampoline_addr) + return pc; + r = &ri->hlist; + hlist_for_each_entry_from(ret_inst, r, hlist) { + if (ret_inst->ret_addr == trampoline_addr) + continue; + /* First handler with a stack pointer lower than the + address (or equal) must be the one. */ + if (ret_inst->sp == sp || compare_stack_ptrs(ret_inst->sp, sp)) + return ret_inst->ret_addr; + } + printk(KERN_ERR "Original return address for trampoline not found at " + "0x%lx pid/tgid=%d/%d\n", sp, current->pid, current->tgid); + return 0; +} + +EXPORT_SYMBOL_GPL(uprobe_get_pc); + #else /* ! CONFIG_URETPROBES */ static void uretprobe_handle_entry(struct uprobe *u, struct pt_regs *regs, diff --git a/runtime/uprobes/uprobes.h b/runtime/uprobes/uprobes.h index d542420d..e888f9e8 100644 --- a/runtime/uprobes/uprobes.h +++ b/runtime/uprobes/uprobes.h @@ -95,6 +95,14 @@ extern void unregister_uretprobe(struct uretprobe *rp); /* For PRs 9940, 6852... */ extern void unmap_uprobe(struct uprobe *u); extern void unmap_uretprobe(struct uretprobe *rp); +/* + * Given a program counter, translate it back to the original address + * if it is the address of the trampoline. sp is the stack pointer for + * the frame that corresponds to the address. + */ +extern unsigned long uprobe_get_pc(struct uretprobe_instance *ri, + unsigned long pc, + unsigned long sp); #ifdef UPROBES_IMPLEMENTATION |