diff options
author | Mark Wielaard <mjw@redhat.com> | 2009-12-21 13:02:19 +0100 |
---|---|---|
committer | Mark Wielaard <mjw@redhat.com> | 2009-12-21 13:24:36 +0100 |
commit | ea549ffc2915aa58861637472b12196222673fa2 (patch) | |
tree | 5bf80c4245ddfd3c12af6f7ee2c4b75075efe86b /runtime | |
parent | 0c487e433fd6343e49b1e9dbc6492f38cfe26143 (diff) | |
download | systemtap-steved-ea549ffc2915aa58861637472b12196222673fa2.tar.gz systemtap-steved-ea549ffc2915aa58861637472b12196222673fa2.tar.xz systemtap-steved-ea549ffc2915aa58861637472b12196222673fa2.zip |
PR11015 Support shared library reloading (in different processes)
* runtime/task_finder_vma.c (stap_remove_vma_map_info): Return negative
status on failure.
(stap_find_vma_map_info): Likewise.
(stap_find_vma_map_info_user): New function.
(stap_drop_vma_maps): New function.
* runtime/sym.h (addr): Renamed to static_addr, to store addresses for
sections which are always mapped at the same address.
(_stp_module_relocate): Add extra struct task_struct *tsk argument.
* runtime/sym.c (_stp_tf_exec_cb): New callback, calls stap_drop_vma_maps.
(_stp_tf_mmap_cb): Don't store address in module.section, but call
stap_add_vma_map_info() per tsk->group_leader for matched module.
Don't register empty/null modules.
(_stp_module_relocate): Take extra struct task_struct *tsk argument,
cache last tsk used. Only use section->static_addr for none dynamic
modules. Use stap_find_vma_map_info_user() to locate dynamic modules.
(_stp_mod_sec_lookup): Add extra argument unsigned long *rel_addr to
optionally store relative address when module/section found.
(_stp_kallsyms_lookup): Use _stp_mod_sec_lookup to find relative address.
(_stp_sym_init): Register _stp_tf_exec_cb in stap_task_finder_target.
Add error check to see if task finder could be initialized.
* dwflpp.cxx (emit_address): Pass NULL for kernel/modules and current for
user tasks to _stp_module_relocate.
* runtime/transport/symbols.c (_stp_do_relocation): Set new static_addr
_stp_section field.
* runtime/unwind.c (adjustStartLoc): Take new struct task_struct *tsk
argument and pass to stap_find_vma_map_info_user and _stp_module_relocate
to find adjusted addr.
(_stp_search_unwind_hdr): Pass through struct task_struct *tsk.
(unwind_frame): Likewise.
* tapset/context-symbols.stp (probemod): Add NULL to _stp_mod_sec_lookup
call to indicate we aren't interested in relative address.
* tapsets.cxx (dwarf_derived_probe_group::emit_module_init): Pass NULL to
_stp_module_relocate to indicate kernel/module address.
Diffstat (limited to 'runtime')
-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 |
5 files changed, 201 insertions, 77 deletions
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); } |