From d1b554876c68f313ec371dbf52c4bddde7bbe10e Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Tue, 9 Mar 2021 12:14:07 -0500 Subject: [PATCH] runtime: fix symbol lookups when the first section isn't executable Some binaries are linked in such a way that there are VMA address range gaps, indicated by non-zero load offsets. The runtime needs to not lose those offsets to enable a proper mapping back & forth from addresses to symbols. --- runtime/sym.c | 12 ++++++------ runtime/task_finder_vma.c | 9 +++++++-- runtime/unwind.c | 2 +- runtime/vma.c | 31 ++++++++++--------------------- 4 files changed, 24 insertions(+), 30 deletions(-) diff --git a/runtime/sym.c b/runtime/sym.c index 1527a9a3d..96805f9ab 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -136,6 +136,7 @@ static struct _stp_module *_stp_kmod_sec_lookup(unsigned long addr, static struct _stp_module *_stp_umod_lookup(unsigned long addr, struct task_struct *task, const char **name, + unsigned long *offset, unsigned long *vm_start, unsigned long *vm_end) { @@ -146,7 +147,7 @@ static struct _stp_module *_stp_umod_lookup(unsigned long addr, addr &= ((compat_ulong_t) ~0); #endif if (stap_find_vma_map_info(task->group_leader, addr, - vm_start, vm_end, name, &user) == 0) + vm_start, vm_end, offset, name, &user) == 0) if (user != NULL) { struct _stp_module *m = (struct _stp_module *)user; @@ -175,6 +176,7 @@ static const char *_stp_kallsyms_lookup(unsigned long addr, if (task) { + unsigned long sect_offset = 0; unsigned long vm_start = 0; unsigned long vm_end = 0; #ifdef CONFIG_COMPAT @@ -184,13 +186,13 @@ static const char *_stp_kallsyms_lookup(unsigned long addr, if (_stp_is_compat_task2(task)) addr &= ((compat_ulong_t) ~0); #endif - m = _stp_umod_lookup(addr, task, modname, &vm_start, &vm_end); + m = _stp_umod_lookup(addr, task, modname, §_offset, &vm_start, &vm_end); if (m) { sec = &m->sections[0]; /* XXX .absolute sections really shouldn't be here... */ if (strcmp(".dynamic", m->sections[0].name) == 0) - rel_addr = addr - vm_start; + rel_addr = addr - vm_start + sect_offset; else rel_addr = addr; } @@ -372,14 +374,12 @@ unsigned long _stp_linenumber_lookup(unsigned long addr, struct task_struct *tas if (task) { - unsigned long vm_start = 0; - unsigned long vm_end = 0; #ifdef CONFIG_COMPAT /* Handle 32bit signed values in 64bit longs, chop off top bits. */ if (_stp_is_compat_task2(task)) addr &= ((compat_ulong_t) ~0); #endif - m = _stp_umod_lookup(addr, task, &modname, &vm_start, &vm_end); + m = _stp_umod_lookup(addr, task, &modname, NULL, NULL, NULL); } else m = _stp_kmod_sec_lookup(addr, &sec); diff --git a/runtime/task_finder_vma.c b/runtime/task_finder_vma.c index dc77a80f5..17aaec386 100644 --- a/runtime/task_finder_vma.c +++ b/runtime/task_finder_vma.c @@ -54,6 +54,7 @@ struct __stp_tf_vma_entry { struct task_struct *tsk; unsigned long vm_start; unsigned long vm_end; + unsigned long offset; char path[TASK_FINDER_VMA_ENTRY_PATHLEN]; /* mmpath name, if known */ // User data (possibly stp_module) @@ -206,7 +207,8 @@ __stp_tf_get_vma_bucket(struct task_struct *tsk) // only from user context. static int stap_add_vma_map_info(struct task_struct *tsk, unsigned long vm_start, - unsigned long vm_end, const char *path, void *user) + unsigned long vm_end, unsigned long offset, + const char *path, void *user) { struct __stp_tf_vma_bucket *bucket = __stp_tf_get_vma_bucket(tsk); struct __stp_tf_vma_entry *entry; @@ -227,6 +229,7 @@ stap_add_vma_map_info(struct task_struct *tsk, unsigned long vm_start, entry->tsk = tsk; entry->vm_start = vm_start; entry->vm_end = vm_end; + entry->offset = offset; entry->user = user; path_len = strlen(path); @@ -290,7 +293,7 @@ stap_remove_vma_map_info(struct task_struct *tsk, unsigned long vm_start) static int stap_find_vma_map_info(struct task_struct *tsk, unsigned long addr, unsigned long *vm_start, unsigned long *vm_end, - const char **path, void **user) + unsigned long *offset, const char **path, void **user) { struct __stp_tf_vma_bucket *bucket; struct __stp_tf_vma_entry *entry; @@ -308,6 +311,8 @@ stap_find_vma_map_info(struct task_struct *tsk, unsigned long addr, *vm_start = entry->vm_start; if (vm_end) *vm_end = entry->vm_end; + if (offset) + *offset = entry->offset; if (path) *path = entry->path; if (user) diff --git a/runtime/unwind.c b/runtime/unwind.c index 9cae157af..dba16a724 100644 --- a/runtime/unwind.c +++ b/runtime/unwind.c @@ -1495,7 +1495,7 @@ static int unwind(struct unwind_context *context, int user) if (user) { - m = _stp_umod_lookup (pc, current, & module_name, NULL, NULL); + m = _stp_umod_lookup (pc, current, & module_name, NULL, NULL, NULL); if (m) s = &m->sections[0]; } diff --git a/runtime/vma.c b/runtime/vma.c index f88145a47..0b7ab1fb1 100644 --- a/runtime/vma.c +++ b/runtime/vma.c @@ -108,7 +108,7 @@ static void _stp_vma_match_vdso(struct task_struct *tsk) if (found != NULL) { stap_add_vma_map_info(tsk, vdso_addr, - vdso_addr + found->sections[0].size, + vdso_addr + found->sections[0].size, 0, "vdso", found); dbug_task_vma(1,"found vdso: %s\n", found->path); } @@ -161,13 +161,13 @@ static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt, // We used to be only interested in the first load of the whole module that // is executable. But with modern enough gcc/ld.so, executables are mapped - // in more small pieces (r--p,r-xp,rw-p, instead of r-xp, rw-p). To establish - // the virtual base address, we initially look for an offset=0 mapping. + // in more small pieces (r--p,r-xp,rw-p, instead of r-xp, rw-p). NB: + // the first section might not be executable, so there can be an offset. // // We register whether or not we know the module, // so we can later lookup the name given an address for this task. - if (path != NULL && offset == 0 - && stap_find_vma_map_info(tsk, addr, NULL, NULL, NULL, NULL) != 0) { + if (path != NULL && + stap_find_vma_map_info(tsk, addr, NULL, NULL, NULL, NULL, NULL) != 0) { for (i = 0; i < _stp_num_modules; i++) { // PR20433: papering over possibility of NULL pointers if (strcmp(path ?: "", _stp_modules[i]->path ?: "") == 0) @@ -178,21 +178,9 @@ static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt, "vm_cb: matched path %s to module (sec: %s)\n", path, _stp_modules[i]->sections[0].name); module = _stp_modules[i]; - /* Make sure we really don't know about this module - yet. If we do know, we might want to extend - the coverage. */ - res = stap_find_vma_map_info_user(tsk->group_leader, - module, - &vm_start, &vm_end, - NULL); - if (res == -ESRCH) - res = stap_add_vma_map_info(tsk->group_leader, - addr, addr + length, - path, module); - else if (res == 0 && vm_end + 1 == addr) - res = stap_extend_vma_map_info(tsk->group_leader, - vm_start, - addr + length); + res = stap_add_vma_map_info(tsk->group_leader, + addr, addr + length, + offset, path, module); /* VMA entries are allocated dynamically, this is fine, * since we are in a task_finder callback, which is in * user context. */ @@ -212,7 +200,8 @@ static int _stp_vma_mmap_cb(struct stap_task_finder_target *tgt, || _stp_target == tsk->group_leader->pid) { res = stap_add_vma_map_info(tsk->group_leader, addr, - addr + length, path, NULL); + addr + length, offset, path, + NULL); dbug_task_vma(1, "registered '%s' for %d (res:%d) [%lx-%lx]\n", path, tsk->group_leader->pid, -- 2.43.5